From 0b1fc1d0cafd5ff460d3376bf1df01a111275e78 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Feb 2022 09:33:27 -0800 Subject: [PATCH 001/278] First pass at getting RfB to work with 3.0 API changes. The API has changed the way instances are accessed. --- rfb_utils/object_utils.py | 26 ++ rman_scene.py | 326 +++++++++++++- rman_scene_sync.py | 426 +++++++++++++++++- .../rman_lightfilter_translator.py | 6 +- 4 files changed, 751 insertions(+), 33 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index f7af94e0..f148c817 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -99,6 +99,7 @@ def is_subdmesh(ob): return False rman_subdiv_scheme = getattr(ob.data.renderman, 'rman_subdiv_scheme', 'none') + return (rman_subdiv_scheme != 'none') if rm.primitive == 'AUTO' and rman_subdiv_scheme == 'none': return (is_subd_last(ob) or is_subd_displace_last(ob)) @@ -140,6 +141,31 @@ def is_transforming(ob, recurse=False): transforming = ob.parent.data.use_path return transforming +def detect_id_type(db, ob=None): + if isinstance(db, bpy.types.Mesh): + return 'MESH' + elif isinstance(db, bpy.types.Light): + if db.renderman.renderman_light_role == 'RMAN_LIGHTFILTER': + return 'LIGHTFILTER' + return 'LIGHT' + elif isinstance(db, bpy.types.Volume): + return 'OPENVDB' + elif ob: + return _detect_primitive_(ob) + + return None + +def prototype_key(ob): + if isinstance(ob, bpy.types.DepsgraphObjectInstance): + if ob.is_instance: + return ob.instance_object.data + if ob.object.data: + return ob.object.data.original + return ob.object.original + elif ob.data: + return ob.original.data.original + return ob.original + def _detect_primitive_(ob): if isinstance(ob, bpy.types.ParticleSystem): diff --git a/rman_scene.py b/rman_scene.py index 5f26662d..c3e0b2b1 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -122,6 +122,7 @@ def __init__(self, rman_render=None): self.rman_cameras = dict() self.obj_hash = dict() self.moving_objects = dict() + self.rman_prototypes = dict() self.processed_obs = [] self.motion_steps = set() @@ -184,6 +185,7 @@ def reset(self): self.obj_hash.clear() self.motion_steps = set() self.moving_objects.clear() + self.rman_prototypes.clear() self.processed_obs.clear() @@ -320,7 +322,8 @@ def export(self): rfb_log().debug("Calling export_data_blocks()") #self.export_data_blocks(bpy.data.objects) - self.export_data_blocks([x for x in self.depsgraph.ids if isinstance(x, bpy.types.Object)]) + #self.export_data_blocks([x for x in self.depsgraph.ids if isinstance(x, bpy.types.Object)]) + self.export_data_blocks() self.export_searchpaths() self.export_global_options() @@ -342,7 +345,7 @@ def export(self): self.export_instances_motion() else: rfb_log().debug("Calling export_instances()") - self.export_instances() + #self.export_instances() self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) self.num_object_instances = len(self.depsgraph.object_instances) @@ -574,16 +577,313 @@ def export_materials(self, materials): if rman_sg_material: self.rman_materials[mat.original] = rman_sg_material - def export_data_blocks(self, data_blocks): + def export_data_blocks_old(self, data_blocks): total = len(data_blocks) for i, obj in enumerate(data_blocks): if obj.type not in ('ARMATURE', 'CAMERA'): ob = obj.evaluated_get(self.depsgraph) - self.export_data_block(ob) + self.export_data_block_old(ob) rfb_log().debug(" Exported %d/%d data blocks... (%s)" % (i, total, obj.name)) self.rman_render.stats_mgr.set_export_stats("Exporting data blocks",i/total) - def export_data_block(self, db_ob): + def export_data_blocks(self, selected_objects=list()): + rman_group_translator = self.rman_translators['GROUP'] + for i, ob_inst in enumerate(self.depsgraph.object_instances): + ob = ob_inst.object + if ob.type in ('ARMATURE', 'CAMERA'): + continue + if selected_objects: + if ob.original not in selected_objects: + continue + + ob_eval = ob.evaluated_get(self.depsgraph) + psys = None + parent = None + proto_key = object_utils.prototype_key(ob_inst) + if ob.type == 'EMPTY': + ob_data = None + else: + ob_data = ob_eval.data.original + + if ob_inst.is_instance: + psys = ob_inst.particle_system + parent = ob_inst.parent + + rman_type = object_utils._detect_primitive_(ob_eval) + #if rman_type == 'LIGHTFILTER': + # skip if this is a light filter + # these will be exported when we do regular lights + # continue + + + rman_sg_node = self.export_data_block(proto_key, ob_eval, ob_data) + if not rman_sg_node: + continue + + + if rman_type == 'EMPTY': + self._export_hidden_instance(ob_eval, rman_sg_node) + continue + elif rman_type == 'LIGHTFILTER': + # we don't need to create instances of light filters + # we just need them to be added as coordinate systems + # which should have been done in export + continue + + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_sg_group = rman_group_translator.export(ob, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + + rman_group_translator.update_transform(ob_inst, rman_sg_group) + + if rman_sg_node.rman_sg_particle_group_node: + if (len(ob.particle_systems) > 0) and ob_inst.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + # Attach a material + if psys: + self.attach_particle_material(psys.settings, parent, ob, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.attach_material(ob, rman_sg_group) + + if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + # this object is a child of an empty. Add it to the empty. + rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) + if not rman_empty_node: + key = ob.parent.original + ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) + rman_empty_node = self.export_data_block(key, ob_parent_eval, None) + self._export_hidden_instance(ob_parent_eval, rman_empty_node) + + if not rman_empty_node: + self.get_root_sg_node().AddChild(rman_sg_group.sg_node) + else: + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + else: + self.get_root_sg_node().AddChild(rman_sg_group.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + + # object attrs + translator = self.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob, rman_sg_group) + translator.export_object_id(ob, rman_sg_group, ob_inst) + + + def export_data_block(self, proto_key, ob, db): + rman_type = object_utils.detect_id_type(db, ob=ob) + + + if proto_key in self.rman_prototypes: + return self.rman_prototypes[proto_key] + + translator = self.rman_translators.get(rman_type, None) + if not translator: + return + + rman_sg_node = None + if not db: + db_name = object_utils.get_db_name(ob) + else: + db_name = db.name + rman_sg_node = translator.export(ob, db_name) + if not rman_sg_node: + return None + rman_sg_node.rman_type = rman_type + self.rman_prototypes[proto_key] = rman_sg_node + translator.update(ob, rman_sg_node) + + + if rman_type in ['MESH', 'POINTS']: + # Deal with any particles now. Particles are children to mesh nodes. + subframes = [] + if self.do_motion_blur: + subframes = scene_utils._get_subframes_(2, self.bl_scene) + self.motion_steps.update(subframes) + + if len(ob.particle_systems) > 0: + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) + + psys_translator = self.rman_translators['PARTICLES'] + for psys in ob.particle_systems: + psys_db_name = '%s' % psys.name + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + if not rman_sg_particles: + continue + + psys_translator.set_motion_steps(rman_sg_particles, subframes) + psys_translator.update(ob, psys, rman_sg_particles) + + ob_psys = self.rman_particles.get(ob.original, dict()) + ob_psys[psys.settings.original] = rman_sg_particles + self.rman_particles[ob.original] = ob_psys + self.rman_objects[psys.settings.original] = rman_sg_particles + self.processed_obs.append(psys.settings.original) + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + elif rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): + # Make sure empties that are hidden still go out. Children + # could still be visible + self._export_hidden_instance(ob, rman_sg_node) + return rman_sg_node + + # motion blur + # we set motion steps for this object, even if it's not moving + # it could be moving as part of a particle system + mb_segs = -1 + mb_deform_segs = -1 + if self.do_motion_blur: + mb_segs = self.bl_scene.renderman.motion_segments + mb_deform_segs = self.bl_scene.renderman.deform_motion_segments + if ob.renderman.motion_segments_override: + mb_segs = ob.renderman.motion_segments + if mb_segs > 1: + subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) + rman_sg_node.motion_steps = subframes + self.motion_steps.update(subframes) + + if ob.renderman.motion_segments_override: + mb_deform_segs = ob.renderman.deform_motion_segments + + if mb_deform_segs > 1: + subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) + rman_sg_node.deform_motion_steps = subframes + self.motion_steps.update(subframes) + + if rman_sg_node.is_transforming or rman_sg_node.is_deforming: + if mb_segs > 1 or mb_deform_segs > 1: + self.moving_objects[ob.name_full] = ob + + if mb_segs < 1: + rman_sg_node.is_transforming = False + if mb_deform_segs < 1: + rman_sg_node.is_deforming = False + + return rman_sg_node + + + def export_instances_motion(self, selected_objects=list()): + origframe = self.bl_scene.frame_current + + mb_segs = self.bl_scene.renderman.motion_segments + origframe = self.bl_scene.frame_current + + motion_steps = sorted(list(self.motion_steps)) + + first_sample = False + delta = -motion_steps[0] + for samp, seg in enumerate(motion_steps): + first_sample = (samp == 0) + if seg < 0.0: + self.rman_render.bl_engine.frame_set(origframe - 1, subframe=1.0 + seg) + else: + self.rman_render.bl_engine.frame_set(origframe, subframe=seg) + + self.depsgraph.update() + time_samp = seg + delta # get the normlized version of the segment + total = len(self.depsgraph.object_instances) + objFound = False + + # update camera + if not first_sample and self.main_camera.is_transforming and seg in self.main_camera.motion_steps: + cam_translator = self.rman_translators['CAMERA'] + idx = 0 + for i, s in enumerate(self.main_camera.motion_steps): + if s == seg: + idx = i + break + cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) + + for i, ob_inst in enumerate(self.depsgraph.object_instances): + if selected_objects: + if objFound: + break + + if ob_inst.is_instance: + if ob_inst.instance_object.name == selected_objects: + objFound = True + elif ob_inst.object.name == selected_objects.name: + objFound = True + + if not objFound: + continue + + if not ob_inst.show_self: + continue + + rman_group_translator = self.rman_translators['GROUP'] + psys = None + proto_key = ob_inst.object.original.data.original + psys = None + parent = None + ob = ob_inst.object + if ob_inst.is_instance: + proto_key = ob_inst.instance_object.data.original + psys = ob_inst.particle_system + parent = ob_inst.parent + + if ob.name_full not in self.moving_objects and not psys: + continue + + if ob.type not in ['MESH']: + continue + + group_db_name = object_utils.get_group_db_name(ob_inst) + + rman_sg_node = self.rman_prototypes.get(proto_key, None) + if not rman_sg_node: + continue + + if not seg in rman_sg_node.motion_steps: + continue + + idx = 0 + for i, s in enumerate(rman_sg_node.motion_steps): + if s == seg: + idx = i + break + + if rman_sg_node.is_transforming or psys: + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if rman_sg_group: + rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() + rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) + + #self.rman_render.stats_mgr.set_export_stats("Exporting instances (%f)" % seg, i/total) + + for ob_original,rman_sg_node in self.rman_objects.items(): + ob = ob_original.evaluated_get(self.depsgraph) + psys_translator = self.rman_translators['PARTICLES'] + particle_systems = getattr(ob, 'particle_systems', list()) + for psys in particle_systems: + ob_psys = self.rman_particles.get(ob.original, dict()) + rman_sg_particles = ob_psys.get(psys.settings.original, None) + if rman_sg_particles: + if not seg in rman_sg_particles.motion_steps: + continue + idx = 0 + for i, s in enumerate(rman_sg_node.motion_steps): + if s == seg: + idx = i + break + psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) + + if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: + rman_type = rman_sg_node.rman_type + if rman_type in ['MESH', 'FLUID']: + translator = self.rman_translators.get(rman_type, None) + if translator: + idx = 0 + for i, s in enumerate(rman_sg_node.deform_motion_steps): + if s == seg: + idx = i + break + translator.export_deform_sample(rman_sg_node, ob, idx) + + self.rman_render.bl_engine.frame_set(origframe, subframe=0) + + def export_data_block_old(self, db_ob): # FIXME? # We currently export a unique geometry/mesh per Object @@ -725,12 +1025,18 @@ def _export_hidden_instance(self, ob, rman_sg_node): translator.export_object_attributes(ob, rman_sg_node) self.attach_material(ob, rman_sg_node) if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': - rman_empty_node = self.rman_objects.get(ob.parent.original) + rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) if not rman_empty_node: # Empty was not created. Export it. - parent = ob.parent - rman_empty_node = self.export_data_block(parent) - rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) + key = ob.parent.original + rman_empty_node = self.export_data_block(key, ob.parent, None) + if not rman_empty_node: + self.get_root_sg_node().AddChild(rman_sg_node.sg_node) + translator.export_transform(ob, rman_sg_node.sg_node) + if ob.renderman.export_as_coordsys: + self.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) + else: + rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_node.sg_node) translator.export_transform(ob, rman_sg_node.sg_node) @@ -937,7 +1243,7 @@ def attach_particle_material(self, psys_settings, parent, ob, group): scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) group.is_meshlight = rman_sg_material.has_meshlight - def export_instances_motion(self, obj_selected=None): + def export_instances_motion_old(self, obj_selected=None): origframe = self.bl_scene.frame_current mb_segs = self.bl_scene.renderman.motion_segments diff --git a/rman_scene_sync.py b/rman_scene_sync.py index e7412f63..4a539379 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -11,6 +11,13 @@ from . import rman_constants import bpy +class RmanUpdate: + def __init__(self): + self.is_updated_geometry = False + self.is_updated_transform = False + self.is_updated_shading = False + self.is_updated_lightfilters = False + class RmanSceneSync(object): ''' The RmanSceneSync class handles keeping the RmanScene object in sync @@ -37,6 +44,9 @@ def __init__(self, rman_render=None, rman_scene=None, sg_scene=None): self.do_delete = False # whether or not we need to do an object deletion self.do_add = False # whether or not we need to add an object self.num_instances_changed = False # if the number of instances has changed since the last update + self.frame_number_changed = False + + self.rman_updates = dict() @property def sg_scene(self): @@ -114,6 +124,7 @@ def _scene_updated(self): rfb_log().debug("Frame changed: %d -> %d" % (self.rman_scene.bl_frame_current, self.rman_scene.bl_scene.frame_current)) self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current material_translator = self.rman_scene.rman_translators["MATERIAL"] + self.frame_number_changed = True with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): # update frame number @@ -127,6 +138,7 @@ def _scene_updated(self): if rman_sg_material and rman_sg_material.is_frame_sensitive: material_translator.update(mat, rman_sg_material) + ''' for o in bpy.data.objects: rman_type = object_utils._detect_primitive_(o) rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) @@ -135,6 +147,7 @@ def _scene_updated(self): translator = self.rman_scene.rman_translators.get(rman_type, None) if translator and rman_sg_node.is_frame_sensitive: translator.update(o, rman_sg_node) + ''' def _mesh_light_update(self, mat): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -188,13 +201,35 @@ def _material_updated(self, obj): rman_sg_material.db_name = db_name def _light_filter_transform_updated(self, obj): - ob = obj.id - rman_sg_lightfilter = self.rman_scene.rman_objects.get(ob.original, None) + ob = obj.id.evaluated_get(self.rman_scene.depsgraph) + rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(ob.original.data.original, None) if rman_sg_lightfilter: rman_group_translator = self.rman_scene.rman_translators['GROUP'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_group_translator.update_transform(ob, rman_sg_lightfilter) + def light_filter_updated(self, obj): + rman_sg_node = self.rman_scene.rman_prototypes.get(obj.id.original.data.original, None) + if not rman_sg_node: + return + ob = obj.id + if obj.is_updated_transform or obj.is_updated_shading: + rfb_log().debug("\tLight Filter: %s Transform Updated" % obj.id.name) + self._light_filter_transform_updated(obj) + if obj.is_updated_geometry: + rfb_log().debug("\tLight Filter: %s Shading Updated" % obj.id.name) + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) + for light_ob in rman_sg_node.lights_list: + if isinstance(light_ob, bpy.types.Material): + rman_sg_material = self.rman_scene.rman_materials.get(light_ob.original, None) + if rman_sg_material: + self.rman_scene.rman_translators['MATERIAL'].update_light_filters(light_ob, rman_sg_material) + else: + rman_sg_light = self.rman_scene.rman_prototypes.get(light_ob.original.data.original, None) + if rman_sg_light: + self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) + def _gpencil_transform_updated(self, obj): ob = obj.id rman_sg_gpencil = self.rman_scene.rman_objects.get(ob.original, None) @@ -317,7 +352,7 @@ def update_object_visibility(self, rman_sg_node, ob): return True return False - def update_particle_settings(self, obj, particle_settings_node): + def update_particle_settings(self, obj, particle_settings_node=None): rfb_log().debug("Check %s for particle settings." % obj.id.name) # A ParticleSettings node was updated. Try to look for it. ob = obj.id @@ -356,10 +391,38 @@ def update_particle_settings(self, obj, particle_settings_node): rman_sg_node = self.rman_scene.rman_objects.get(obj.id.original, None) for instance_obj in rman_sg_node.objects_instanced: self.clear_instances(instance_obj) - self.update_instances.add(instance_obj) + self.update_instances.add(instance_obj) + + def check_particle_systems(self, ob_update): + ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) + for psys in ob.particle_systems: + if object_utils.is_particle_instancer(psys): + # this particle system is a instancer, add the instanced object + # to the self.update_instances list + inst_ob = getattr(psys.settings, 'instance_object', None) + collection = getattr(psys.settings, 'instance_collection', None) + if inst_ob: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[inst_ob.original] = rman_update + elif collection: + for col_obj in collection.all_objects: + if not col_obj.original.data: + continue + if col_obj.original in self.rman_updates: + continue + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[col_obj.original] = rman_update + continue def update_particle_systems(self): + ''' with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): for ob in self.update_particles: rman_type = object_utils._detect_primitive_(ob) @@ -420,9 +483,39 @@ def update_particle_systems(self): ob_psys[psys.settings.original] = rman_sg_particles self.rman_scene.rman_particles[ob.original] = ob_psys rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + ''' - def update_empty(self, ob, rman_sg_node=None): + def update_empty(self, ob_update, rman_sg_node=None): + ob = ob_update.id rfb_log().debug("Update empty: %s" % ob.name) + if ob.is_instancer: + rfb_log().debug("\tEmpty is an instancer") + collection = ob.instance_collection + if collection: + for col_obj in collection.all_objects: + if not col_obj.original.data: + continue + if col_obj.original in self.rman_updates: + continue + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[col_obj.original] = rman_update + else: + rfb_log().debug("\tRegular empty") + rman_sg_node = self.rman_prototypes.get(ob.original, None) + if not rman_sg_node: + return + translator = self.rman_scene.rman_translators['EMPTY'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + translator.export_transform(ob, rman_sg_node.sg_node) + if ob.renderman.export_as_coordsys: + self.rman_scene.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) + else: + self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) + + ''' if ob.is_instancer: collection = ob.instance_collection if collection: @@ -448,6 +541,7 @@ def update_empty(self, ob, rman_sg_node=None): self.rman_scene.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) else: self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) + ''' def reemit_instances(self): # update instances @@ -457,6 +551,56 @@ def reemit_instances(self): # Re-emit instances for all objects in self.update_instances rfb_log().debug("Re-emit instances") rman_group_translator = self.rman_scene.rman_translators['GROUP'] + for ob_inst in self.rman_scene.depsgraph.object_instances: + db = ob_inst.object.data + ob = ob_inst.object + rman_type = object_utils.detect_id_type(db) + if not ob_inst.object.original.data: + continue + proto_key = ob_inst.object.original.data.original + datablock = ob_inst.object.data + psys = None + parent = None + + ob_key = ob_inst.object.original + if ob_inst.is_instance: + if ob_inst.instance_object.original not in self.update_instances: + continue + proto_key = ob_inst.instance_object.data.original + ob_key = ob_inst.instance_object.original + psys = ob_inst.particle_system + parent = ob_inst.parent + elif ob_key not in self.update_instances: + continue + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if not rman_sg_node: + rman_sg_node = self.rman_scene.export_data_block(proto_key, ob, db) + if not rman_sg_node: + continue + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if not rman_sg_group: + rman_sg_group = rman_group_translator.export(ob, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + + rman_group_translator.update_transform(ob_inst, rman_sg_group) + + # Attach a material + if psys: + self.rman_scene.attach_particle_material(psys.settings, parent, ob, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.rman_scene.attach_material(ob, rman_sg_group) + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_sg_node.rman_sg_particle_group_node: + rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + if (len(ob.particle_systems) > 0) and ob_inst.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + + + ''' for ob_inst in self.rman_scene.depsgraph.object_instances: parent = None if ob_inst.is_instance: @@ -487,6 +631,7 @@ def reemit_instances(self): continue self.rman_scene._export_instance(ob_inst) + ''' def clear_instances(self, ob, rman_sg_node=None): rfb_log().debug("Deleting instances") @@ -495,8 +640,9 @@ def clear_instances(self, ob, rman_sg_node=None): rman_sg_node = self.rman_scene.rman_objects.get(ob.original) for k,rman_sg_group in rman_sg_node.instances.items(): if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': - rman_empty_node = self.rman_scene.rman_objects.get(ob.parent.original) - rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) + pass + #rman_empty_node = self.rman_scene.rman_objects.get(ob.parent.original) + #rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) else: self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) rman_sg_node.instances.clear() @@ -608,10 +754,12 @@ def update_scene(self, context, depsgraph): self.new_cameras.clear() self.update_instances.clear() self.update_particles.clear() + self.rman_updates = dict() self.do_delete = False # whether or not we need to do an object deletion self.do_add = False # whether or not we need to add an object self.num_instances_changed = False # if the number of instances has changed since the last update + self.frame_number_changed = False self.rman_scene.depsgraph = depsgraph self.rman_scene.bl_scene = depsgraph.scene @@ -620,7 +768,7 @@ def update_scene(self, context, depsgraph): particle_settings_node = None did_mesh_update = False # did the mesh actually update prev_num_instances = self.rman_scene.num_object_instances # the number of instances previously - + # Check the number of instances. If we differ, an object may have been # added or deleted if self.rman_scene.num_object_instances != len(depsgraph.object_instances): @@ -697,8 +845,36 @@ def update_scene(self, context, depsgraph): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) # Save this particle settings node, so we can check for it later # when we process object changes - particle_settings_node = obj.id.original + #particle_settings_node = obj.id.original + users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + psys_translator = self.rman_scene.rman_translators['PARTICLES'] + for o in users[obj.id.original]: + psys = None + ob = o.evaluated_get(depsgraph) + for ps in ob.particle_systems: + if ps.settings.original == obj.id.original: + psys = ps + break + if not psys: + continue + psys_db_name = '%s' % psys.name + ob_psys = self.rman_scene.rman_particles.get(ob.original, dict()) + rman_sg_particles = ob_psys.get(obj.id.original, None) + if not rman_sg_particles: + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + self.rman_scene.rman_particles[ob.original] = ob_psys + proto_key = o.original.data.original + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if rman_sg_node: + if not rman_sg_node.rman_sg_particle_group_node: + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = self.rman_scene.rman_translators['GROUP'].export(None, particles_group_db) + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + psys_translator.update(ob, psys, rman_sg_particles) + + elif isinstance(obj.id, bpy.types.ShaderNodeTree): if obj.id.name in bpy.data.node_groups: # this is probably one of our fake node groups with ramps @@ -712,6 +888,54 @@ def update_scene(self, context, depsgraph): o.node_tree.update_tag() elif isinstance(obj.id, bpy.types.Object): + ob_data = bpy.data.objects.get(ob.name, ob) + rman_type = object_utils._detect_primitive_(ob_data) + if rman_type == 'EMPTY': + rfb_log().debug("\tEmpty: %s Updated" % obj.id.name) + self.update_empty(obj) + continue + elif rman_type == 'LIGHTFILTER': + rfb_log().debug("\tLight Filter: %s Updated" % obj.id.name) + self.light_filter_updated(obj) + continue + if ob.type in ('ARMATURE'): + continue + elif ob.type in ['CAMERA']: + # we deal with main camera transforms in view_draw + rman_sg_camera = self.rman_scene.rman_cameras[ob.original] + if rman_sg_camera == self.rman_scene.main_camera: + continue + translator = self.rman_scene.rman_translators['CAMERA'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + translator._update_render_cam_transform(ob, rman_sg_camera) + continue + + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + if obj.id.data: + proto_key = obj.id.original.data.original + else: + proto_key = obj.id.original + rfb_log().debug("\tObject: %s Updated" % obj.id.name) + self.rman_updates[obj.id.original] = rman_update + + self.check_particle_systems(obj) + + # Check if this object is the focus object the camera. If it is + # we need to update the camera + rman_sg_camera = self.rman_scene.main_camera + if rman_sg_camera.rman_focus_object and rman_sg_camera.rman_focus_object == rman_sg_node: + translator = self.rman_scene.rman_translators['CAMERA'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + cam_object = translator.find_scene_camera() + translator.update(cam_object, rman_sg_camera) + + + continue + + particle_systems = getattr(obj.id, 'particle_systems', list()) has_particle_systems = len(particle_systems) > 0 @@ -859,45 +1083,206 @@ def update_scene(self, context, depsgraph): continue rfb_log().debug("Collection updated: %s" % obj.id.name) - self.update_collection(obj.id) + #self.update_collection(obj.id) else: - self.update_geometry_node_instances(obj.id) + pass + #self.update_geometry_node_instances(obj.id) + + + deleted_obj_keys = list() + already_udpated = list() + clear_instances = list() + if self.num_instances_changed or self.rman_updates: + deleted_obj_keys = list(self.rman_scene.rman_prototypes) + rfb_log().debug("Updating instances") + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + rman_group_translator = self.rman_scene.rman_translators['GROUP'] + for instance in depsgraph.object_instances: + if instance.object.type in ('ARMATURE', 'CAMERA'): + continue + + ob_key = instance.object.original + ob_eval = instance.object.evaluated_get(depsgraph) + parent = None + psys = None + is_new_object = False + proto_key = object_utils.prototype_key(instance) + if instance.is_instance: + ob_key = instance.instance_object.original + psys = instance.particle_system + parent = instance.parent + + if proto_key in deleted_obj_keys: + deleted_obj_keys.remove(proto_key) + + db = None + if instance.object.data: + db = instance.object.data + rman_type = object_utils._detect_primitive_(ob_eval) + + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if not rman_sg_node: + # this is a new object. + rfb_log().debug("\tAdding: %s" % proto_key.name) + rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval, db) + if not rman_sg_node: + continue + is_new_object = True + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + rman_update.is_updated_geometry = False + + if not self.num_instances_changed: + if ob_key not in self.rman_updates: + if not parent: + continue + # check if the parent is also marked to be updated + if parent.original not in self.rman_updates: + continue + + # parent was marked needing update. + # create an on the fly RmanUpdate() + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + else: + rman_update = self.rman_updates[ob_key] + else: + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + + if rman_sg_node and not is_new_object: + if rman_update.is_updated_geometry and proto_key not in already_udpated: + translator = self.rman_scene.rman_translators.get(rman_type, None) + rfb_log().debug("\tUpdating Object: %s" % proto_key.name) + translator.update(ob_eval, rman_sg_node) + already_udpated.append(proto_key) + + + if rman_sg_node not in clear_instances: + # clear all instances + for k,rman_sg_group in rman_sg_node.instances.items(): + if ob_eval.parent and object_utils._detect_primitive_(ob_eval.parent) == 'EMPTY': + pass + else: + self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) + rman_sg_node.instances.clear() + clear_instances.append(rman_sg_node) + + group_db_name = object_utils.get_group_db_name(instance) + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if not rman_sg_group: + rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + rman_group_translator.update_transform(instance, rman_sg_group) + + # Attach a material + if psys: + self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.rman_scene.attach_material(ob_eval, rman_sg_group) + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_sg_node.rman_sg_particle_group_node: + rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + if (len(ob_eval.particle_systems) > 0) and instance.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + + # delete stuff + if deleted_obj_keys: + self.delete_objects(deleted_obj_keys) # call txmake all in case of new textures texture_utils.get_txmanager().txmake_all(blocking=False) # add new objs: if self.new_objects: - self.add_objects() + pass + #self.add_objects() elif self.do_add: # if we didn't detect any new objects, but the number of # instances changed, check our existing objects for object # deletion and/or visibility - self.delete_objects() + pass + #self.delete_objects() # delete any objects, if necessary if self.do_delete: - self.delete_objects() + pass + #self.delete_objects() # update any particle systems - self.update_particle_systems() + #self.update_particle_systems() # re-emit any instances needed - self.reemit_instances() - + #self.reemit_instances() + # + rfb_log().debug("------End update scene----------") def add_objects(self): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rfb_log().debug("Adding new objects:") - self.rman_scene.export_data_blocks(self.new_objects) + self.rman_scene.export_data_blocks(selected_objects=self.new_objects) self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() if self.rman_scene.scene_any_lights: self.rman_scene.default_light.SetHidden(1) - def delete_objects(self): + def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("Deleting objects") + for key in deleted_obj_keys: + rman_sg_node = self.rman_scene.rman_prototypes.get(key, None) + if not rman_sg_node: + continue + + rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) + for k,v in rman_sg_node.instances.items(): + if v.sg_node: + self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) + rman_sg_node.instances.clear() + + # For now, don't delete the geometry itself + # there may be a collection instance still referencing the geo + + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) + del self.rman_scene.rman_prototypes[key] + + ''' + # We just deleted a light filter. We need to tell all lights + # associated with this light filter to update + if isinstance(rman_sg_node, RmanSgLightFilter): + for light_ob in rman_sg_node.lights_list: + light_key = object_utils.get_db_name(light_ob, rman_type='LIGHT') + rman_sg_light = self.rman_scene.rman_objects.get(light_ob.original, None) + if rman_sg_light: + self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) + try: + self.rman_scene.processed_obs.remove(obj) + except ValueError: + rfb_log().debug("Obj not in self.rman_scene.processed_obs: %s") + pass + ''' + + if self.rman_scene.render_default_light: + self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() + if not self.rman_scene.scene_any_lights: + self.rman_scene.default_light.SetHidden(0) + + ''' with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): keys = [k for k in self.rman_scene.rman_objects.keys()] for obj in keys: @@ -944,7 +1329,8 @@ def delete_objects(self): if self.rman_scene.render_default_light: self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() if not self.rman_scene.scene_any_lights: - self.rman_scene.default_light.SetHidden(0) + self.rman_scene.default_light.SetHidden(0) + ''' def update_cropwindow(self, cropwindow=None): if not self.rman_render.rman_interactive_running: diff --git a/rman_translators/rman_lightfilter_translator.py b/rman_translators/rman_lightfilter_translator.py index 6c7ea7c9..9e2d80cf 100644 --- a/rman_translators/rman_lightfilter_translator.py +++ b/rman_translators/rman_lightfilter_translator.py @@ -33,7 +33,7 @@ def export_light_filters(self, ob, rman_sg_node, rm): light_filter_sg = None light_filter_db_name = object_utils.get_db_name(light_filter) - rman_sg_lightfilter = self.rman_scene.rman_objects.get(light_filter.original) + rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(light_filter.original.data.original) if not rman_sg_lightfilter: rman_sg_lightfilter = self.export(light_filter, light_filter_db_name) elif not isinstance(rman_sg_lightfilter, RmanSgLightFilter): @@ -43,7 +43,7 @@ def export_light_filters(self, ob, rman_sg_node, rm): self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) rman_sg_lightfilter.instances.clear() del rman_sg_lightfilter - self.rman_scene.rman_objects.pop(light_filter.original) + self.rman_scene.rman_prototypes.pop(light_filter.original.data.original) rman_sg_lightfilter = self.export(light_filter, light_filter_db_name) self.update(light_filter, rman_sg_lightfilter) light_filters.append(rman_sg_lightfilter.sg_filter_node) @@ -93,7 +93,7 @@ def export(self, ob, db_name): rman_group_translator.update_transform(ob, rman_sg_lightfilter) self.rman_scene.get_root_sg_node().AddChild(rman_sg_lightfilter.sg_node) - self.rman_scene.rman_objects[ob.original] = rman_sg_lightfilter + self.rman_scene.rman_prototypes[ob.original.data.original] = rman_sg_lightfilter self.rman_scene.sg_scene.Root().AddCoordinateSystem(rman_sg_lightfilter.sg_node) return rman_sg_lightfilter From 613a9c959f40a9d1c2056bb9b45dd2ec19814fad Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Feb 2022 13:01:51 -0800 Subject: [PATCH 002/278] Handle deleting/undo deleting of light filters --- rman_scene.py | 9 +++++---- rman_scene_sync.py | 46 +++++++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index c3e0b2b1..f35827b9 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -683,10 +683,11 @@ def export_data_block(self, proto_key, ob, db): return rman_sg_node = None - if not db: - db_name = object_utils.get_db_name(ob) - else: - db_name = db.name + #if not db: + # db_name = object_utils.get_db_name(ob) + #else: + # db_name = db.name + db_name = object_utils.get_db_name(ob) rman_sg_node = translator.export(ob, db_name) if not rman_sg_node: return None diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 4a539379..0b2fd330 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -95,6 +95,7 @@ def _scene_updated(self): self.rman_scene.check_solo_light() # Check view_layer + ''' view_layer = self.rman_scene.depsgraph.view_layer if len(view_layer.objects) != self.rman_scene.num_objects_in_viewlayer: # objects can be removed from the viewlayer by hiding a collection. @@ -117,6 +118,7 @@ def _scene_updated(self): continue self.rman_scene.objects_in_viewlayer = [o for o in view_layer.objects] + ''' if self.rman_scene.bl_frame_current != self.rman_scene.bl_scene.frame_current: # frame changed, update any materials and objects that @@ -911,6 +913,7 @@ def update_scene(self, context, depsgraph): continue rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry rman_update.is_updated_shading = obj.is_updated_shading rman_update.is_updated_transform = obj.is_updated_transform @@ -919,6 +922,9 @@ def update_scene(self, context, depsgraph): else: proto_key = obj.id.original rfb_log().debug("\tObject: %s Updated" % obj.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) self.rman_updates[obj.id.original] = rman_update self.check_particle_systems(obj) @@ -1128,6 +1134,15 @@ def update_scene(self, context, depsgraph): rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval, db) if not rman_sg_node: continue + + if rman_type == 'LIGHTFILTER': + # update all lights with this light filter + users = bpy.context.blend_data.user_map(subset={ob_eval.original}) + for o in users[ob_eval.original]: + if isinstance(o, bpy.types.Light): + o.node_tree.update_tag() + continue + is_new_object = True rman_update = self.rman_updates.get(ob_key, None) if not rman_update: @@ -1136,7 +1151,7 @@ def update_scene(self, context, depsgraph): rman_update.is_updated_transform = True self.rman_updates[ob_key] = rman_update rman_update.is_updated_geometry = False - + if not self.num_instances_changed: if ob_key not in self.rman_updates: if not parent: @@ -1168,9 +1183,13 @@ def update_scene(self, context, depsgraph): translator.update(ob_eval, rman_sg_node) already_udpated.append(proto_key) - + if rman_type == 'LIGHTFILTER': + # Light filters are special + continue + if rman_sg_node not in clear_instances: # clear all instances + rfb_log().debug("\tClearing instances: %s" % proto_key.name) for k,rman_sg_group in rman_sg_node.instances.items(): if ob_eval.parent and object_utils._detect_primitive_(ob_eval.parent) == 'EMPTY': pass @@ -1258,24 +1277,21 @@ def delete_objects(self, deleted_obj_keys=list()): # For now, don't delete the geometry itself # there may be a collection instance still referencing the geo + self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_node.sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) del self.rman_scene.rman_prototypes[key] - - ''' + # We just deleted a light filter. We need to tell all lights # associated with this light filter to update if isinstance(rman_sg_node, RmanSgLightFilter): - for light_ob in rman_sg_node.lights_list: - light_key = object_utils.get_db_name(light_ob, rman_type='LIGHT') - rman_sg_light = self.rman_scene.rman_objects.get(light_ob.original, None) - if rman_sg_light: - self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) - try: - self.rman_scene.processed_obs.remove(obj) - except ValueError: - rfb_log().debug("Obj not in self.rman_scene.processed_obs: %s") - pass - ''' + self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) + for o in rman_sg_node.lights_list: + if o: + if hasattr(o, 'rman_nodetree'): + o.rman_nodetree.update_tag() + elif hasattr(o.data, 'node_tree'): + o.data.node_tree.update_tag() + if self.rman_scene.render_default_light: self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() From 7cca80d6b36c1a5f90ec8d01e8ccb7e9eae13e7a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Feb 2022 16:09:29 -0800 Subject: [PATCH 003/278] Handle visiblity of collections correctly during IPR. We use the visible_objects list to figure out what's still in the scene. --- rman_scene.py | 24 ++++++--- rman_scene_sync.py | 132 +++++++++++++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 53 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index f35827b9..959bb087 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -349,8 +349,8 @@ def export(self): self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) self.num_object_instances = len(self.depsgraph.object_instances) - self.num_objects_in_viewlayer = len(self.depsgraph.view_layer.objects) - self.objects_in_viewlayer = [o for o in self.depsgraph.view_layer.objects] + self.num_objects_in_viewlayer = len(self.context.visible_objects) + self.objects_in_viewlayer = [o for o in self.context.visible_objects] self.check_solo_light() if self.is_interactive: @@ -586,6 +586,20 @@ def export_data_blocks_old(self, data_blocks): rfb_log().debug(" Exported %d/%d data blocks... (%s)" % (i, total, obj.name)) self.rman_render.stats_mgr.set_export_stats("Exporting data blocks",i/total) + def check_visibility(self, instance): + if not self.is_interactive: + return True + viewport = self.context.space_data + ob_eval = instance.object.evaluated_get(self.depsgraph) + + if instance.is_instance: + ob_eval = instance.instance_object.original.evaluated_get(self.depsgraph) + if instance.parent: + ob_eval = instance.parent + + visible = ob_eval.visible_in_viewport_get(viewport) + return visible + def export_data_blocks(self, selected_objects=list()): rman_group_translator = self.rman_translators['GROUP'] for i, ob_inst in enumerate(self.depsgraph.object_instances): @@ -610,10 +624,8 @@ def export_data_blocks(self, selected_objects=list()): parent = ob_inst.parent rman_type = object_utils._detect_primitive_(ob_eval) - #if rman_type == 'LIGHTFILTER': - # skip if this is a light filter - # these will be exported when we do regular lights - # continue + if not self.check_visibility(ob_inst): + continue rman_sg_node = self.export_data_block(proto_key, ob_eval, ob_data) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 0b2fd330..3057a02d 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -80,8 +80,11 @@ def _scene_updated(self): for ob in self.rman_scene.bl_scene.objects: if ob.type in ('ARMATURE', 'CURVE', 'CAMERA', 'LIGHT'): continue - self.clear_instances(ob) - self.update_instances.add(ob.original) + if ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob.original] = rman_update with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.check_solo_light() elif not self.rman_scene.bl_local_view and (self.rman_scene.context.space_data.local_view is not None): @@ -89,36 +92,38 @@ def _scene_updated(self): for ob in self.rman_scene.bl_scene.objects: if ob.type in ('ARMATURE', 'CURVE', 'CAMERA', 'LIGHT'): continue - self.clear_instances(ob) - self.update_instances.add(ob.original) + if ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob.original] = rman_update with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.check_solo_light() # Check view_layer - ''' - view_layer = self.rman_scene.depsgraph.view_layer - if len(view_layer.objects) != self.rman_scene.num_objects_in_viewlayer: - # objects can be removed from the viewlayer by hiding a collection. + visible_objects = self.rman_scene.context.visible_objects + if len(visible_objects) != self.rman_scene.num_objects_in_viewlayer: # Figure out the difference using sets and re-emit their instances. - self.rman_scene.num_objects_in_viewlayer = len(view_layer.objects) - view_layer = self.rman_scene.depsgraph.view_layer + self.rman_scene.num_objects_in_viewlayer = len(visible_objects) + #view_layer = self.rman_scene.depsgraph.view_layer set1 = set(self.rman_scene.objects_in_viewlayer) - set2 = set((view_layer.objects)) + set2 = set(visible_objects) #set((view_layer.objects)) set_diff1 = set1.difference(set2) set_diff2 = set2.difference(set1) objects = list(set_diff1.union(set_diff2)) for o in list(objects): try: - self.update_instances.add(o.original) - self.clear_instances(o) - self.update_particles.add(o) - self.update_geometry_node_instances(o) + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[o.original] = rman_update except: continue + self.num_instances_changed = True - self.rman_scene.objects_in_viewlayer = [o for o in view_layer.objects] - ''' + self.rman_scene.objects_in_viewlayer = [o for o in visible_objects] if self.rman_scene.bl_frame_current != self.rman_scene.bl_scene.frame_current: # frame changed, update any materials and objects that @@ -142,13 +147,11 @@ def _scene_updated(self): ''' for o in bpy.data.objects: - rman_type = object_utils._detect_primitive_(o) - rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) - if not rman_sg_node: - continue - translator = self.rman_scene.rman_translators.get(rman_type, None) - if translator and rman_sg_node.is_frame_sensitive: - translator.update(o, rman_sg_node) + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[o.original] = rman_update ''' def _mesh_light_update(self, mat): @@ -404,11 +407,12 @@ def check_particle_systems(self, ob_update): inst_ob = getattr(psys.settings, 'instance_object', None) collection = getattr(psys.settings, 'instance_collection', None) if inst_ob: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry - rman_update.is_updated_shading = ob_update.is_updated_shading - rman_update.is_updated_transform = ob_update.is_updated_transform - self.rman_updates[inst_ob.original] = rman_update + if inst_ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[inst_ob.original] = rman_update elif collection: for col_obj in collection.all_objects: if not col_obj.original.data: @@ -690,9 +694,15 @@ def update_collection(self, coll): # FIXME: like grease pencil above we seem to crash when removing and adding instances # of curves, we need to figure out what's going on for o in coll.all_objects: - if o.type in ('ARMATURE', 'CURVE', 'CAMERA'): + if o.type in ('ARMATURE', 'CAMERA'): continue + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[o.original] = rman_update + ''' rman_type = object_utils._detect_primitive_(o) rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) if not rman_sg_node: @@ -710,6 +720,7 @@ def update_collection(self, coll): self.update_instances.add(o.original) self.update_particles.add(o) self.update_geometry_node_instances(o) + ''' def update_geometry_node_instances(self, obj): def update_geo_instances(nodes): @@ -773,6 +784,7 @@ def update_scene(self, context, depsgraph): # Check the number of instances. If we differ, an object may have been # added or deleted + ''' if self.rman_scene.num_object_instances != len(depsgraph.object_instances): self.num_instances_changed = True if self.rman_scene.num_object_instances > len(depsgraph.object_instances): @@ -780,6 +792,7 @@ def update_scene(self, context, depsgraph): else: self.do_add = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) + ''' rfb_log().debug("------Start update scene--------") for obj in reversed(depsgraph.updates): @@ -861,20 +874,42 @@ def update_scene(self, context, depsgraph): break if not psys: continue - psys_db_name = '%s' % psys.name - ob_psys = self.rman_scene.rman_particles.get(ob.original, dict()) - rman_sg_particles = ob_psys.get(obj.id.original, None) - if not rman_sg_particles: - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - self.rman_scene.rman_particles[ob.original] = ob_psys - proto_key = o.original.data.original - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) - if rman_sg_node: - if not rman_sg_node.rman_sg_particle_group_node: - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_scene.rman_translators['GROUP'].export(None, particles_group_db) - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - psys_translator.update(ob, psys, rman_sg_particles) + if object_utils.is_particle_instancer(psys): + inst_ob = getattr(psys.settings, 'instance_object', None) + collection = getattr(psys.settings, 'instance_collection', None) + if inst_ob: + if inst_ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[inst_ob.original] = rman_update + elif collection: + for col_obj in collection.all_objects: + if not col_obj.original.data: + continue + if col_obj.original in self.rman_updates: + continue + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[col_obj.original] = rman_update + else: + psys_db_name = '%s' % psys.name + ob_psys = self.rman_scene.rman_particles.get(ob.original, dict()) + rman_sg_particles = ob_psys.get(obj.id.original, None) + if not rman_sg_particles: + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + self.rman_scene.rman_particles[ob.original] = ob_psys + proto_key = o.original.data.original + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if rman_sg_node: + if not rman_sg_node.rman_sg_particle_group_node: + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = self.rman_scene.rman_translators['GROUP'].export(None, particles_group_db) + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + psys_translator.update(ob, psys, rman_sg_particles) elif isinstance(obj.id, bpy.types.ShaderNodeTree): @@ -951,7 +986,7 @@ def update_scene(self, context, depsgraph): ob_data = bpy.data.objects.get(ob.name, ob) rman_sg_node = self.rman_scene.rman_objects.get(obj.id.original, None) - # NOTE: hide_get() and hide_viewport are two different things in Blender + # NOTE:hide _get() and hide_viewport are two different things in Blender # hide_get() hides the object from the viewport, but it does not actually remove the object # as instances of the object can still be visible (ex: in particle systems) # hide_viewport should be interpreted as an actual deleted object, including @@ -1113,9 +1148,9 @@ def update_scene(self, context, depsgraph): parent = None psys = None is_new_object = False - proto_key = object_utils.prototype_key(instance) + proto_key = object_utils.prototype_key(instance) if instance.is_instance: - ob_key = instance.instance_object.original + ob_key = instance.instance_object.original psys = instance.particle_system parent = instance.parent @@ -1198,6 +1233,9 @@ def update_scene(self, context, depsgraph): rman_sg_node.instances.clear() clear_instances.append(rman_sg_node) + if not self.rman_scene.check_visibility(instance): + continue + group_db_name = object_utils.get_group_db_name(instance) rman_sg_group = rman_sg_node.instances.get(group_db_name, None) if not rman_sg_group: From 1cf7273f44d75afc51f9afe4fbd6a2cb5c518a26 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Feb 2022 16:19:04 -0800 Subject: [PATCH 004/278] Uncomment the check on the length of depsgraph.object_instances. We still need this check in case a particle instancer is removed. --- rman_scene_sync.py | 48 ++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 3057a02d..26f9ab05 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -100,29 +100,30 @@ def _scene_updated(self): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.check_solo_light() - # Check view_layer + # Check visible objects visible_objects = self.rman_scene.context.visible_objects - if len(visible_objects) != self.rman_scene.num_objects_in_viewlayer: - # Figure out the difference using sets and re-emit their instances. - self.rman_scene.num_objects_in_viewlayer = len(visible_objects) - #view_layer = self.rman_scene.depsgraph.view_layer - set1 = set(self.rman_scene.objects_in_viewlayer) - set2 = set(visible_objects) #set((view_layer.objects)) - set_diff1 = set1.difference(set2) - set_diff2 = set2.difference(set1) - - objects = list(set_diff1.union(set_diff2)) - for o in list(objects): - try: - if o.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[o.original] = rman_update - except: - continue - self.num_instances_changed = True + if not self.num_instances_changed: + if len(visible_objects) != self.rman_scene.num_objects_in_viewlayer: + # The number of visible objects has changed. + # Figure out the difference using sets + set1 = set(self.rman_scene.objects_in_viewlayer) + set2 = set(visible_objects) + set_diff1 = set1.difference(set2) + set_diff2 = set2.difference(set1) + + objects = list(set_diff1.union(set_diff2)) + for o in list(objects): + try: + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[o.original] = rman_update + except: + continue + self.num_instances_changed = True + self.rman_scene.num_objects_in_viewlayer = len(visible_objects) self.rman_scene.objects_in_viewlayer = [o for o in visible_objects] if self.rman_scene.bl_frame_current != self.rman_scene.bl_scene.frame_current: @@ -402,8 +403,7 @@ def check_particle_systems(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) for psys in ob.particle_systems: if object_utils.is_particle_instancer(psys): - # this particle system is a instancer, add the instanced object - # to the self.update_instances list + # this particle system is a instancer inst_ob = getattr(psys.settings, 'instance_object', None) collection = getattr(psys.settings, 'instance_collection', None) if inst_ob: @@ -784,7 +784,6 @@ def update_scene(self, context, depsgraph): # Check the number of instances. If we differ, an object may have been # added or deleted - ''' if self.rman_scene.num_object_instances != len(depsgraph.object_instances): self.num_instances_changed = True if self.rman_scene.num_object_instances > len(depsgraph.object_instances): @@ -792,7 +791,6 @@ def update_scene(self, context, depsgraph): else: self.do_add = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) - ''' rfb_log().debug("------Start update scene--------") for obj in reversed(depsgraph.updates): From 4fd2cc20e91e5f6d7fe35cc2dbb5a1cfc0c39963 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 07:55:18 -0800 Subject: [PATCH 005/278] Deal with camera changes, and solo/muting lights. --- rman_scene.py | 26 +++++++++-- rman_scene_sync.py | 111 +++++++++++++++++++++++++-------------------- 2 files changed, 84 insertions(+), 53 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 959bb087..58c998ee 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -351,7 +351,6 @@ def export(self): self.num_object_instances = len(self.depsgraph.object_instances) self.num_objects_in_viewlayer = len(self.context.visible_objects) self.objects_in_viewlayer = [o for o in self.context.visible_objects] - self.check_solo_light() if self.is_interactive: self.export_viewport_stats() @@ -602,8 +601,11 @@ def check_visibility(self, instance): def export_data_blocks(self, selected_objects=list()): rman_group_translator = self.rman_translators['GROUP'] + total = len(self.depsgraph.object_instances) for i, ob_inst in enumerate(self.depsgraph.object_instances): ob = ob_inst.object + rfb_log().debug(" Exported %d/%d instances... (%s)" % (i, total, ob.name)) + self.rman_render.stats_mgr.set_export_stats("Exporting instances",i/total) if ob.type in ('ARMATURE', 'CAMERA'): continue if selected_objects: @@ -627,6 +629,8 @@ def export_data_blocks(self, selected_objects=list()): if not self.check_visibility(ob_inst): continue + if rman_type == 'LIGHT': + self.check_solo_light(rman_sg_node, ob_eval) rman_sg_node = self.export_data_block(proto_key, ob_eval, ob_data) if not rman_sg_node: @@ -809,6 +813,8 @@ def export_instances_motion(self, selected_objects=list()): cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) for i, ob_inst in enumerate(self.depsgraph.object_instances): + rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) + self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) if selected_objects: if objFound: break @@ -863,8 +869,6 @@ def export_instances_motion(self, selected_objects=list()): rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) - #self.rman_render.stats_mgr.set_export_stats("Exporting instances (%f)" % seg, i/total) - for ob_original,rman_sg_node in self.rman_objects.items(): ob = ob_original.evaluated_get(self.depsgraph) psys_translator = self.rman_translators['PARTICLES'] @@ -1388,7 +1392,20 @@ def check_light_local_view(self, ob, rman_sg_node): return False - def check_solo_light(self): + def check_solo_light(self, rman_sg_node, ob): + if not self.scene_solo_light: + rman_sg_node.sg_node.SetHidden(ob.renderman.mute) + else: + rm = ob.renderman + if not rm: + return + if rm.solo: + rman_sg_node.sg_node.SetHidden(0) + else: + rman_sg_node.sg_node.SetHidden(1) + + + ''' if self.bl_scene.renderman.solo_light: for light_ob in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False): rman_sg_node = self.rman_objects.get(light_ob.original, None) @@ -1420,6 +1437,7 @@ def check_solo_light(self): rman_sg_node.sg_node.SetHidden(1) else: rman_sg_node.sg_node.SetHidden(rm.mute) + ''' def export_searchpaths(self): # TODO diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 26f9ab05..57f00c69 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -236,6 +236,33 @@ def light_filter_updated(self, obj): if rman_sg_light: self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) + def camera_updated(self, ob_update): + ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + rman_sg_camera = self.rman_scene.rman_cameras.get(ob.original) + translator = self.rman_scene.rman_translators['CAMERA'] + + if not rman_sg_camera: + db_name = object_utils.get_db_name(ob) + rman_sg_camera = translator._export_render_cam(ob, db_name) + self.rman_scene.rman_cameras[ob.original] = rman_sg_camera + + self.rman_scene.sg_scene.Root().AddChild(rman_sg_camera.sg_node) + self.rman_scene.sg_scene.Root().AddCoordinateSystem(rman_sg_camera.sg_node) + return + + if ob_update.is_updated_geometry: + if not self.rman_scene.is_viewport_render: + translator.update(ob, rman_sg_camera) + else: + translator.update_viewport_cam(ob, rman_sg_camera, force_update=True) + + if ob_update.is_updated_transform: + # we deal with main camera transforms in view_draw + if rman_sg_camera == self.rman_scene.main_camera: + return + translator._update_render_cam_transform(ob, rman_sg_camera) + def _gpencil_transform_updated(self, obj): ob = obj.id rman_sg_gpencil = self.rman_scene.rman_objects.get(ob.original, None) @@ -307,6 +334,18 @@ def _obj_geometry_updated(self, obj): def update_light_visibility(self, rman_sg_node, ob): if not self.rman_scene.scene_solo_light: + rman_sg_node.sg_node.SetHidden(ob.renderman.mute) + else: + rm = ob.renderman + if not rm: + return + if rm.solo: + rman_sg_node.sg_node.SetHidden(0) + else: + rman_sg_node.sg_node.SetHidden(1) + + + ''' vis = rman_sg_node.sg_node.GetHidden() if vis == -1: vis = 0 @@ -331,6 +370,7 @@ def update_light_visibility(self, rman_sg_node, ob): if update_instances and len(rman_sg_node.instances) < 1: self.update_instances.add(ob.original) return result + ''' def update_object_visibility(self, rman_sg_node, ob): ob_data = bpy.data.objects.get(ob.name, ob) @@ -925,24 +965,22 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.Object): ob_data = bpy.data.objects.get(ob.name, ob) rman_type = object_utils._detect_primitive_(ob_data) + + if ob.type in ('ARMATURE'): + continue + + # These types need special handling if rman_type == 'EMPTY': rfb_log().debug("\tEmpty: %s Updated" % obj.id.name) self.update_empty(obj) continue - elif rman_type == 'LIGHTFILTER': + if rman_type == 'LIGHTFILTER': rfb_log().debug("\tLight Filter: %s Updated" % obj.id.name) self.light_filter_updated(obj) continue - if ob.type in ('ARMATURE'): - continue - elif ob.type in ['CAMERA']: - # we deal with main camera transforms in view_draw - rman_sg_camera = self.rman_scene.rman_cameras[ob.original] - if rman_sg_camera == self.rman_scene.main_camera: - continue - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator._update_render_cam_transform(ob, rman_sg_camera) + if ob.type in ['CAMERA']: + rfb_log().debug("\tCamera updated: %s" % obj.id.name) + self.camera_updated(obj) continue rman_update = RmanUpdate() @@ -1233,6 +1271,9 @@ def update_scene(self, context, depsgraph): if not self.rman_scene.check_visibility(instance): continue + + if rman_type == 'LIGHT': + self.rman_scene.check_solo_light(rman_sg_node, ob_eval) group_db_name = object_utils.get_group_db_name(instance) rman_sg_group = rman_sg_node.instances.get(group_db_name, None) @@ -1244,6 +1285,11 @@ def update_scene(self, context, depsgraph): rman_group_translator.update_transform(instance, rman_sg_group) + if rman_type == 'LIGHT': + if self.rman_scene.default_light.GetHidden() != 1: + self.rman_scene.default_light.SetHidden(1) + continue + # Attach a material if psys: self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) @@ -1464,27 +1510,12 @@ def update_material(self, mat): def update_light(self, ob): if not self.rman_render.rman_interactive_running: return - rman_sg_light = self.rman_scene.rman_objects.get(ob.original, None) - if not rman_sg_light: - return - translator = self.rman_scene.rman_translators["LIGHT"] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator.update(ob, rman_sg_light) - + ob.data.node_tree.update_tag() + def update_light_filter(self, ob): if not self.rman_render.rman_interactive_running: return - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) - if not rman_sg_node: - return - - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) - for light_ob in rman_sg_node.lights_list: - light_key = object_utils.get_db_name(light_ob, rman_type='LIGHT') - rman_sg_light = self.rman_scene.rman_objects.get(light_ob.original, None) - if rman_sg_light: - self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) + ob.data.node_tree.update_tag() def update_solo_light(self, context): if not self.rman_render.rman_interactive_running: @@ -1495,17 +1526,7 @@ def update_solo_light(self, context): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): for light_ob in scene_utils.get_all_lights(self.rman_scene.bl_scene, include_light_filters=False): - rman_sg_node = self.rman_scene.rman_objects.get(light_ob.original, None) - if not rman_sg_node: - continue - rm = light_ob.renderman - if not rm: - continue - - if rm.solo: - rman_sg_node.sg_node.SetHidden(0) - else: - rman_sg_node.sg_node.SetHidden(1) + light_ob.update_tag() def update_un_solo_light(self, context): if not self.rman_render.rman_interactive_running: @@ -1516,15 +1537,7 @@ def update_un_solo_light(self, context): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): for light_ob in scene_utils.get_all_lights(self.rman_scene.bl_scene, include_light_filters=False): - rman_sg_node = self.rman_scene.rman_objects.get(light_ob.original, None) - if not rman_sg_node: - continue - rm = light_ob.renderman - if not rm: - continue - if self.rman_scene.check_light_local_view(light_ob, rman_sg_node): - continue - rman_sg_node.sg_node.SetHidden(light_ob.hide_get()) + light_ob.update_tag() def update_viewport_chan(self, context, chan_name): if not self.rman_render.rman_interactive_running: From d1c6616d19decc418ac870c2ac42a53962fb50f7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 08:36:25 -0800 Subject: [PATCH 006/278] We need to call check_solo_light after we've exported the datablock. Also, for instances, we need to use object.data as our dictionary key. --- rfb_utils/object_utils.py | 2 +- rman_scene.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index f148c817..a60fc99f 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -158,7 +158,7 @@ def detect_id_type(db, ob=None): def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): if ob.is_instance: - return ob.instance_object.data + return ob.object.data.original if ob.object.data: return ob.object.data.original return ob.object.original diff --git a/rman_scene.py b/rman_scene.py index 58c998ee..49942895 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -625,16 +625,16 @@ def export_data_blocks(self, selected_objects=list()): psys = ob_inst.particle_system parent = ob_inst.parent - rman_type = object_utils._detect_primitive_(ob_eval) if not self.check_visibility(ob_inst): - continue - - if rman_type == 'LIGHT': - self.check_solo_light(rman_sg_node, ob_eval) + continue + rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.export_data_block(proto_key, ob_eval, ob_data) if not rman_sg_node: - continue + continue + + if rman_type == 'LIGHT': + self.check_solo_light(rman_sg_node, ob_eval) if rman_type == 'EMPTY': @@ -699,10 +699,6 @@ def export_data_block(self, proto_key, ob, db): return rman_sg_node = None - #if not db: - # db_name = object_utils.get_db_name(ob) - #else: - # db_name = db.name db_name = object_utils.get_db_name(ob) rman_sg_node = translator.export(ob, db_name) if not rman_sg_node: From 20b6d3f8e674296de149a56cb1779277fab49a83 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 09:00:33 -0800 Subject: [PATCH 007/278] Fix the _mesh_light_update function --- rman_scene_sync.py | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 57f00c69..88f66100 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -156,20 +156,16 @@ def _scene_updated(self): ''' def _mesh_light_update(self, mat): + object_list = list() with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for ob_inst in self.rman_scene.depsgraph.object_instances: - psys = None - if ob_inst.is_instance: - ob = ob_inst.instance_object - group_db_name = object_utils.get_group_db_name(ob_inst) - else: - ob = ob_inst.object - group_db_name = object_utils.get_group_db_name(ob_inst) + for ob_inst in self.rman_scene.depsgraph.object_instances: + ob = ob_inst.object.evaluated_get(self.rman_scene.depsgraph) if not hasattr(ob.data, 'materials'): continue if ob.type in ('ARMATURE', 'CURVE', 'CAMERA'): - continue - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) + continue + proto_key = object_utils.prototype_key(ob_inst) + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if rman_sg_node: found = False for name, material in ob.data.materials.items(): @@ -177,11 +173,19 @@ def _mesh_light_update(self, mat): found = True if found: - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if rman_sg_group: - rman_sg_node.instances.pop(group_db_name) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) - self.rman_scene._export_instance(ob_inst) + for k,rman_sg_group in rman_sg_node.instances.items(): + if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + pass + else: + self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) + rman_sg_node.instances.clear() + del self.rman_scene.rman_prototypes[proto_key] + if ob not in object_list: + object_list.append(ob) + + for ob in object_list: + ob.update_tag() def _material_updated(self, obj): mat = obj.id @@ -1266,6 +1270,7 @@ def update_scene(self, context, depsgraph): pass else: self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) rman_sg_node.instances.clear() clear_instances.append(rman_sg_node) @@ -1356,9 +1361,6 @@ def delete_objects(self, deleted_obj_keys=list()): self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) rman_sg_node.instances.clear() - # For now, don't delete the geometry itself - # there may be a collection instance still referencing the geo - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_node.sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) del self.rman_scene.rman_prototypes[key] From 118ba28f2906c3ec8cd65b469273baa9c8a1587f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 09:18:55 -0800 Subject: [PATCH 008/278] Merge some code into clear_instances --- rman_scene_sync.py | 51 ++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 88f66100..74c9abdf 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -173,13 +173,7 @@ def _mesh_light_update(self, mat): found = True if found: - for k,rman_sg_group in rman_sg_node.instances.items(): - if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': - pass - else: - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) - rman_sg_node.instances.clear() + self.clear_instances(ob, rman_sg_node) del self.rman_scene.rman_prototypes[proto_key] if ob not in object_list: object_list.append(ob) @@ -684,18 +678,16 @@ def reemit_instances(self): ''' def clear_instances(self, ob, rman_sg_node=None): - rfb_log().debug("Deleting instances") - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - if not rman_sg_node: - rman_sg_node = self.rman_scene.rman_objects.get(ob.original) - for k,rman_sg_group in rman_sg_node.instances.items(): - if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': - pass - #rman_empty_node = self.rman_scene.rman_objects.get(ob.parent.original) - #rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) - else: - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) - rman_sg_node.instances.clear() + for k,rman_sg_group in rman_sg_node.instances.items(): + if ob and ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + proto_key = object_utils.prototype_key(ob.parent) + rman_empty_node = self.rman_scene.rman_prototypes.get(proto_key) + if rman_empty_node: + rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) + else: + self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) + rman_sg_node.instances.clear() def update_materials_dict(self, mat): # See comment below in update_objects_dict @@ -1171,9 +1163,9 @@ def update_scene(self, context, depsgraph): #self.update_geometry_node_instances(obj.id) - deleted_obj_keys = list() - already_udpated = list() - clear_instances = list() + deleted_obj_keys = list() # list of potential objects to delete + already_udpated = list() # list of objects already updated during our loop + clear_instances = list() # list of objects who had their instances cleared if self.num_instances_changed or self.rman_updates: deleted_obj_keys = list(self.rman_scene.rman_prototypes) rfb_log().debug("Updating instances") @@ -1265,13 +1257,7 @@ def update_scene(self, context, depsgraph): if rman_sg_node not in clear_instances: # clear all instances rfb_log().debug("\tClearing instances: %s" % proto_key.name) - for k,rman_sg_group in rman_sg_node.instances.items(): - if ob_eval.parent and object_utils._detect_primitive_(ob_eval.parent) == 'EMPTY': - pass - else: - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) - rman_sg_node.instances.clear() + self.clear_instances(ob_eval, rman_sg_node) clear_instances.append(rman_sg_node) if not self.rman_scene.check_visibility(instance): @@ -1355,11 +1341,8 @@ def delete_objects(self, deleted_obj_keys=list()): if not rman_sg_node: continue - rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) - for k,v in rman_sg_node.instances.items(): - if v.sg_node: - self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) - rman_sg_node.instances.clear() + rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) + self.clear_instances(None, rman_sg_node) self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_node.sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) From e261e4b8a7f955a33d46d3b3c62999907ad0f775 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 09:45:38 -0800 Subject: [PATCH 009/278] Deal with the edge case where the rman_types don't match. --- rman_scene.py | 5 +++-- rman_scene_sync.py | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 49942895..3104d2db 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -349,8 +349,9 @@ def export(self): self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) self.num_object_instances = len(self.depsgraph.object_instances) - self.num_objects_in_viewlayer = len(self.context.visible_objects) - self.objects_in_viewlayer = [o for o in self.context.visible_objects] + visible_objects = getattr(self.context, 'visible_objects', list()) + self.num_objects_in_viewlayer = len(visible_objects) + self.objects_in_viewlayer = [o for o in visible_objects] if self.is_interactive: self.export_viewport_stats() diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 74c9abdf..b7462aac 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -548,7 +548,7 @@ def update_empty(self, ob_update, rman_sg_node=None): self.rman_updates[col_obj.original] = rman_update else: rfb_log().debug("\tRegular empty") - rman_sg_node = self.rman_prototypes.get(ob.original, None) + rman_sg_node = self.rman_scene.rman_prototypes.get(ob.original, None) if not rman_sg_node: return translator = self.rman_scene.rman_translators['EMPTY'] @@ -1195,8 +1195,24 @@ def update_scene(self, context, depsgraph): rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if rman_sg_node and rman_type != rman_sg_node.rman_type: + # Types don't match + # + # This can happen because + # we have not been able to tag our types before Blender + # tells us an object has been added + rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key.name) + self.clear_instances(ob_eval, rman_sg_node) + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) + del self.rman_scene.rman_prototypes[proto_key] + rman_sg_node = None + if not rman_sg_node: # this is a new object. + if rman_type == 'LIGHT': + # We don't support adding new Blender lights + if not shadergraph_utils.is_rman_light(ob_eval): + continue rfb_log().debug("\tAdding: %s" % proto_key.name) rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval, db) if not rman_sg_node: From 0a490e237b0a7083393d0a043ae38a2b2915bc43 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 09:56:15 -0800 Subject: [PATCH 010/278] Add some logging to the camera_updated function --- rman_scene_sync.py | 194 ++------------------------------------------- 1 file changed, 5 insertions(+), 189 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index b7462aac..9a482f74 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -241,6 +241,7 @@ def camera_updated(self, ob_update): translator = self.rman_scene.rman_translators['CAMERA'] if not rman_sg_camera: + rfb_log().debug("\tNew Camera: %s" % ob.name) db_name = object_utils.get_db_name(ob) rman_sg_camera = translator._export_render_cam(ob, db_name) self.rman_scene.rman_cameras[ob.original] = rman_sg_camera @@ -250,6 +251,7 @@ def camera_updated(self, ob_update): return if ob_update.is_updated_geometry: + rfb_log().debug("\tUpdated Camera: %s" % ob.name) if not self.rman_scene.is_viewport_render: translator.update(ob, rman_sg_camera) else: @@ -259,6 +261,7 @@ def camera_updated(self, ob_update): # we deal with main camera transforms in view_draw if rman_sg_camera == self.rman_scene.main_camera: return + rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) translator._update_render_cam_transform(ob, rman_sg_camera) def _gpencil_transform_updated(self, obj): @@ -464,71 +467,6 @@ def check_particle_systems(self, ob_update): self.rman_updates[col_obj.original] = rman_update continue - def update_particle_systems(self): - - ''' - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for ob in self.update_particles: - rman_type = object_utils._detect_primitive_(ob) - if rman_type not in ['MESH', 'POINTS']: - continue - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) - ob_eval = ob.evaluated_get(self.rman_scene.depsgraph) - rfb_log().debug("Update particle systems for: %s" % ob.name) - - # any objects that this object instanced, need to update their instances - for instance_obj in rman_sg_node.objects_instanced: - self.clear_instances(instance_obj) - self.update_instances.add(instance_obj) - - if rman_sg_node.rman_sg_particle_group_node: - rman_sg_node.rman_sg_particle_group_node.sg_node.RemoveAllChildren() - - if len(ob_eval.particle_systems) < 1: - continue - - if not rman_sg_node.rman_sg_particle_group_node: - db_name = rman_sg_node.db_name - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_scene.rman_translators['GROUP'].export(None, particles_group_db) - rman_sg_node.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - psys_translator = self.rman_scene.rman_translators['PARTICLES'] - - for psys in ob_eval.particle_systems: - if object_utils.is_particle_instancer(psys): - # this particle system is a instancer, add the instanced object - # to the self.update_instances list - inst_ob = getattr(psys.settings, 'instance_object', None) - collection = getattr(psys.settings, 'instance_collection', None) - if inst_ob: - self.update_instances.add(inst_ob.original) - rman_instance_sg_node = self.rman_scene.rman_objects.get(inst_ob.original, None) - if rman_instance_sg_node: - self.clear_instances(inst_ob.original, rman_instance_sg_node) - elif collection: - for col_obj in collection.all_objects: - self.update_instances.add(col_obj.original) - rman_instance_sg_node = self.rman_scene.rman_objects.get(col_obj.original, None) - if rman_instance_sg_node: - self.clear_instances(col_obj.original, rman_instance_sg_node) - else: - self.new_objects.add(col_obj.original) - continue - - ob_psys = self.rman_scene.rman_particles.get(ob_eval.original, dict()) - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if not rman_sg_particles: - psys_db_name = '%s' % psys.name - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - if not rman_sg_particles: - continue - psys_translator.update(ob, psys, rman_sg_particles) - ob_psys[psys.settings.original] = rman_sg_particles - self.rman_scene.rman_particles[ob.original] = ob_psys - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - ''' - def update_empty(self, ob_update, rman_sg_node=None): ob = ob_update.id rfb_log().debug("Update empty: %s" % ob.name) @@ -587,96 +525,6 @@ def update_empty(self, ob_update, rman_sg_node=None): self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) ''' - def reemit_instances(self): - # update instances - if not self.update_instances: - return - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - # Re-emit instances for all objects in self.update_instances - rfb_log().debug("Re-emit instances") - rman_group_translator = self.rman_scene.rman_translators['GROUP'] - for ob_inst in self.rman_scene.depsgraph.object_instances: - db = ob_inst.object.data - ob = ob_inst.object - rman_type = object_utils.detect_id_type(db) - if not ob_inst.object.original.data: - continue - proto_key = ob_inst.object.original.data.original - datablock = ob_inst.object.data - psys = None - parent = None - - ob_key = ob_inst.object.original - if ob_inst.is_instance: - if ob_inst.instance_object.original not in self.update_instances: - continue - proto_key = ob_inst.instance_object.data.original - ob_key = ob_inst.instance_object.original - psys = ob_inst.particle_system - parent = ob_inst.parent - elif ob_key not in self.update_instances: - continue - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) - if not rman_sg_node: - rman_sg_node = self.rman_scene.export_data_block(proto_key, ob, db) - if not rman_sg_node: - continue - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if not rman_sg_group: - rman_sg_group = rman_group_translator.export(ob, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group - - rman_group_translator.update_transform(ob_inst, rman_sg_group) - - # Attach a material - if psys: - self.rman_scene.attach_particle_material(psys.settings, parent, ob, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original - else: - self.rman_scene.attach_material(ob, rman_sg_group) - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group - if rman_sg_node.rman_sg_particle_group_node: - rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - if (len(ob.particle_systems) > 0) and ob_inst.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - - ''' - for ob_inst in self.rman_scene.depsgraph.object_instances: - parent = None - if ob_inst.is_instance: - ob = ob_inst.instance_object - parent = ob_inst.parent - else: - ob = ob_inst.object - - if ob.original not in self.update_instances: - continue - - rman_type = object_utils._detect_primitive_(ob) - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) - if rman_sg_node: - translator = self.rman_scene.rman_translators.get(rman_type, None) - translator.export_object_primvars(ob, rman_sg_node) - - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if rman_sg_group: - rman_group_translator.update_transform(ob_inst, rman_sg_group) - # object attrs - rman_group_translator.export_object_attributes(ob, rman_sg_group) - if rman_sg_group.bl_psys_settings: - self.rman_scene.attach_particle_material(rman_sg_group.bl_psys_settings, parent, ob, rman_sg_group) - else: - self.rman_scene.attach_material(ob, rman_sg_group) - continue - - self.rman_scene._export_instance(ob_inst) - ''' - def clear_instances(self, ob, rman_sg_node=None): for k,rman_sg_group in rman_sg_node.instances.items(): if ob and ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': @@ -1315,40 +1163,8 @@ def update_scene(self, context, depsgraph): self.delete_objects(deleted_obj_keys) # call txmake all in case of new textures - texture_utils.get_txmanager().txmake_all(blocking=False) - # add new objs: - if self.new_objects: - pass - #self.add_objects() - elif self.do_add: - # if we didn't detect any new objects, but the number of - # instances changed, check our existing objects for object - # deletion and/or visibility - pass - #self.delete_objects() - - # delete any objects, if necessary - if self.do_delete: - pass - #self.delete_objects() - - # update any particle systems - #self.update_particle_systems() - - # re-emit any instances needed - #self.reemit_instances() - # - - rfb_log().debug("------End update scene----------") - - def add_objects(self): - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - rfb_log().debug("Adding new objects:") - self.rman_scene.export_data_blocks(selected_objects=self.new_objects) - - self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() - if self.rman_scene.scene_any_lights: - self.rman_scene.default_light.SetHidden(1) + texture_utils.get_txmanager().txmake_all(blocking=False) + rfb_log().debug("------End update scene----------") def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("Deleting objects") From b3264a0c7038a128e5c851fa79f3672866343461 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 10:06:41 -0800 Subject: [PATCH 011/278] Fix convert cycles shader operator. 3.0 no longer has a cycles_visibility attribute. --- rman_operators/rman_operators_nodetree.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/rman_operators/rman_operators_nodetree.py b/rman_operators/rman_operators_nodetree.py index d2d56596..716567c1 100644 --- a/rman_operators/rman_operators_nodetree.py +++ b/rman_operators/rman_operators_nodetree.py @@ -43,12 +43,20 @@ def execute(self, context): # convert cycles vis settings for ob in context.scene.objects: - if not ob.cycles_visibility.camera: - ob.renderman.visibility_camera = False - if not ob.cycles_visibility.diffuse or not ob.cycles_visibility.glossy: - ob.renderman.visibility_trace_indirect = False - if not ob.cycles_visibility.transmission: - ob.renderman.visibility_trace_transmission = False + if hasattr(ob, 'cycles_visibility'): + if not ob.cycles_visibility.camera: + ob.renderman.rman_visibilityCamera = "0" + if not ob.cycles_visibility.diffuse or not ob.cycles_visibility.glossy: + ob.renderman.rman_visibilityIndirect = "0" + if not ob.cycles_visibility.transmission: + ob.renderman.rman_visibilityTransmission = "0" + else: + if not ob.visible_camera: + ob.renderman.rman_visibilityCamera = "0" + if not ob.visible_diffuse or not ob.visible_glossy: + ob.renderman.rman_visibilityIndirect = "0" + if not ob.visible_transmission: + ob.renderman.rman_visibilityTransmission = "0" if ob.type == 'LIGHT' and not ob.data.use_nodes: if ob.data.type == 'POINT': From 36a77b1426493b0bf9431ed78569ae6fecf190ca Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 10:16:52 -0800 Subject: [PATCH 012/278] Logging print was in the wrong place for export_instances_motion. --- rman_scene.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 3104d2db..78c17739 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -809,9 +809,7 @@ def export_instances_motion(self, selected_objects=list()): break cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) - for i, ob_inst in enumerate(self.depsgraph.object_instances): - rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) - self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) + for i, ob_inst in enumerate(self.depsgraph.object_instances): if selected_objects: if objFound: break @@ -834,6 +832,8 @@ def export_instances_motion(self, selected_objects=list()): psys = None parent = None ob = ob_inst.object + rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) + self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) if ob_inst.is_instance: proto_key = ob_inst.instance_object.data.original psys = ob_inst.particle_system From 569ffa4108da14da875028c952aec185b35b864d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 13:38:45 -0800 Subject: [PATCH 013/278] Fix deformation blur. Change prototype_key to use the data name. The data attribute can be different frame to frame, so we switch to using the name. --- rfb_utils/object_utils.py | 10 +- rman_scene.py | 161 +++++++++++------------ rman_scene_sync.py | 8 +- rman_translators/rman_hair_translator.py | 2 +- 4 files changed, 89 insertions(+), 92 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index a60fc99f..d44d3527 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -158,13 +158,13 @@ def detect_id_type(db, ob=None): def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): if ob.is_instance: - return ob.object.data.original + return ob.object.data.name if ob.object.data: - return ob.object.data.original - return ob.object.original + return ob.object.data.name + return ob.object.original.name elif ob.data: - return ob.original.data.original - return ob.original + return ob.original.data.original.name + return ob.original.name def _detect_primitive_(ob): diff --git a/rman_scene.py b/rman_scene.py index 78c17739..9425ddc3 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -706,8 +706,40 @@ def export_data_block(self, proto_key, ob, db): return None rman_sg_node.rman_type = rman_type self.rman_prototypes[proto_key] = rman_sg_node - translator.update(ob, rman_sg_node) + # motion blur + # we set motion steps for this object, even if it's not moving + # it could be moving as part of a particle system + mb_segs = -1 + mb_deform_segs = -1 + if self.do_motion_blur: + mb_segs = self.bl_scene.renderman.motion_segments + mb_deform_segs = self.bl_scene.renderman.deform_motion_segments + if ob.renderman.motion_segments_override: + mb_segs = ob.renderman.motion_segments + if mb_segs > 1: + subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) + rman_sg_node.motion_steps = subframes + self.motion_steps.update(subframes) + + if ob.renderman.motion_segments_override: + mb_deform_segs = ob.renderman.deform_motion_segments + + if mb_deform_segs > 1: + subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) + rman_sg_node.deform_motion_steps = subframes + self.motion_steps.update(subframes) + + if rman_sg_node.is_transforming or rman_sg_node.is_deforming: + if mb_segs > 1 or mb_deform_segs > 1: + self.moving_objects[ob.name_full] = ob + + if mb_segs < 1: + rman_sg_node.is_transforming = False + if mb_deform_segs < 1: + rman_sg_node.is_deforming = False + + translator.update(ob, rman_sg_node) if rman_type in ['MESH', 'POINTS']: # Deal with any particles now. Particles are children to mesh nodes. @@ -740,39 +772,7 @@ def export_data_block(self, proto_key, ob, db): # Make sure empties that are hidden still go out. Children # could still be visible self._export_hidden_instance(ob, rman_sg_node) - return rman_sg_node - - # motion blur - # we set motion steps for this object, even if it's not moving - # it could be moving as part of a particle system - mb_segs = -1 - mb_deform_segs = -1 - if self.do_motion_blur: - mb_segs = self.bl_scene.renderman.motion_segments - mb_deform_segs = self.bl_scene.renderman.deform_motion_segments - if ob.renderman.motion_segments_override: - mb_segs = ob.renderman.motion_segments - if mb_segs > 1: - subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) - rman_sg_node.motion_steps = subframes - self.motion_steps.update(subframes) - - if ob.renderman.motion_segments_override: - mb_deform_segs = ob.renderman.deform_motion_segments - - if mb_deform_segs > 1: - subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) - rman_sg_node.deform_motion_steps = subframes - self.motion_steps.update(subframes) - - if rman_sg_node.is_transforming or rman_sg_node.is_deforming: - if mb_segs > 1 or mb_deform_segs > 1: - self.moving_objects[ob.name_full] = ob - - if mb_segs < 1: - rman_sg_node.is_transforming = False - if mb_deform_segs < 1: - rman_sg_node.is_deforming = False + return rman_sg_node return rman_sg_node @@ -787,6 +787,8 @@ def export_instances_motion(self, selected_objects=list()): first_sample = False delta = -motion_steps[0] + psys_translator = self.rman_translators['PARTICLES'] + rman_group_translator = self.rman_translators['GROUP'] for samp, seg in enumerate(motion_steps): first_sample = (samp == 0) if seg < 0.0: @@ -826,76 +828,71 @@ def export_instances_motion(self, selected_objects=list()): if not ob_inst.show_self: continue - rman_group_translator = self.rman_translators['GROUP'] psys = None - proto_key = ob_inst.object.original.data.original - psys = None - parent = None - ob = ob_inst.object + ob = ob_inst.object.evaluated_get(self.depsgraph) + proto_key = object_utils.prototype_key(ob_inst) rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) if ob_inst.is_instance: proto_key = ob_inst.instance_object.data.original psys = ob_inst.particle_system - parent = ob_inst.parent + # check particles for motion + for psys in ob.particle_systems: + ob_psys = self.rman_particles.get(ob.original, None) + if not ob_psys: + continue + rman_sg_particles = ob_psys.get(psys.settings.original, None) + if not rman_sg_particles: + continue + if not seg in rman_sg_particles.motion_steps: + continue + idx = 0 + for i, s in enumerate(rman_sg_particles.motion_steps): + if s == seg: + idx = i + break + psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) + + # object is not moving and not part of a particle system if ob.name_full not in self.moving_objects and not psys: continue - - if ob.type not in ['MESH']: - continue - - group_db_name = object_utils.get_group_db_name(ob_inst) - + rman_sg_node = self.rman_prototypes.get(proto_key, None) if not rman_sg_node: continue - if not seg in rman_sg_node.motion_steps: - continue - - idx = 0 - for i, s in enumerate(rman_sg_node.motion_steps): - if s == seg: - idx = i - break - - if rman_sg_node.is_transforming or psys: - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if rman_sg_group: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() - rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) - - for ob_original,rman_sg_node in self.rman_objects.items(): - ob = ob_original.evaluated_get(self.depsgraph) - psys_translator = self.rman_translators['PARTICLES'] - particle_systems = getattr(ob, 'particle_systems', list()) - for psys in particle_systems: - ob_psys = self.rman_particles.get(ob.original, dict()) - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if rman_sg_particles: - if not seg in rman_sg_particles.motion_steps: - continue - idx = 0 - for i, s in enumerate(rman_sg_node.motion_steps): - if s == seg: - idx = i - break - psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) - + # transformation blur + if seg in rman_sg_node.motion_steps: + idx = 0 + for i, s in enumerate(rman_sg_node.motion_steps): + if s == seg: + idx = i + break + + if rman_sg_node.is_transforming or psys: + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if rman_sg_group: + rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() + rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) + + # deformation blur if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: rman_type = rman_sg_node.rman_type if rman_type in ['MESH', 'FLUID']: translator = self.rman_translators.get(rman_type, None) if translator: - idx = 0 + deform_idx = 0 for i, s in enumerate(rman_sg_node.deform_motion_steps): if s == seg: - idx = i - break - translator.export_deform_sample(rman_sg_node, ob, idx) + deform_idx = i + break + translator.export_deform_sample(rman_sg_node, ob, deform_idx) self.rman_render.bl_engine.frame_set(origframe, subframe=0) + rfb_log().debug(" Finished exporting motion instances") + self.rman_render.stats_mgr.set_export_stats("Finished exporting motion instances", 100) def export_data_block_old(self, db_ob): diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 9a482f74..da9d56ed 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -1049,7 +1049,7 @@ def update_scene(self, context, depsgraph): # This can happen because # we have not been able to tag our types before Blender # tells us an object has been added - rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key.name) + rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key) self.clear_instances(ob_eval, rman_sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) del self.rman_scene.rman_prototypes[proto_key] @@ -1061,7 +1061,7 @@ def update_scene(self, context, depsgraph): # We don't support adding new Blender lights if not shadergraph_utils.is_rman_light(ob_eval): continue - rfb_log().debug("\tAdding: %s" % proto_key.name) + rfb_log().debug("\tAdding: %s" % proto_key) rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval, db) if not rman_sg_node: continue @@ -1110,7 +1110,7 @@ def update_scene(self, context, depsgraph): if rman_sg_node and not is_new_object: if rman_update.is_updated_geometry and proto_key not in already_udpated: translator = self.rman_scene.rman_translators.get(rman_type, None) - rfb_log().debug("\tUpdating Object: %s" % proto_key.name) + rfb_log().debug("\tUpdating Object: %s" % proto_key) translator.update(ob_eval, rman_sg_node) already_udpated.append(proto_key) @@ -1120,7 +1120,7 @@ def update_scene(self, context, depsgraph): if rman_sg_node not in clear_instances: # clear all instances - rfb_log().debug("\tClearing instances: %s" % proto_key.name) + rfb_log().debug("\tClearing instances: %s" % proto_key) self.clear_instances(ob_eval, rman_sg_node) clear_instances.append(rman_sg_node) diff --git a/rman_translators/rman_hair_translator.py b/rman_translators/rman_hair_translator.py index 3745d3c5..242e6fb8 100644 --- a/rman_translators/rman_hair_translator.py +++ b/rman_translators/rman_hair_translator.py @@ -32,7 +32,7 @@ def clear_children(self, ob, psys, rman_sg_hair): def export_deform_sample(self, rman_sg_hair, ob, psys, time_sample): curves = self._get_strands_(ob, psys) - for i, (vertsArray, points, widths, scalpST) in enumerate(curves): + for i, (vertsArray, points, widths, scalpST, mcols) in enumerate(curves): curves_sg = rman_sg_hair.sg_curves_list[i] if not curves_sg: continue From 76b5c5dd55f4454f4c2fa1e74bfca2387b41d924 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 14:03:43 -0800 Subject: [PATCH 014/278] Use prototype_key to get the rman_sg_node when dealing with particles. --- rman_scene.py | 4 +--- rman_scene_sync.py | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 9425ddc3..7ae66fd2 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -764,9 +764,7 @@ def export_data_block(self, proto_key, ob, db): ob_psys = self.rman_particles.get(ob.original, dict()) ob_psys[psys.settings.original] = rman_sg_particles - self.rman_particles[ob.original] = ob_psys - self.rman_objects[psys.settings.original] = rman_sg_particles - self.processed_obs.append(psys.settings.original) + self.rman_particles[ob.original] = ob_psys rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) elif rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): # Make sure empties that are hidden still go out. Children diff --git a/rman_scene_sync.py b/rman_scene_sync.py index da9d56ed..f4af9f3a 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -783,8 +783,9 @@ def update_scene(self, context, depsgraph): rman_sg_particles = ob_psys.get(obj.id.original, None) if not rman_sg_particles: rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + ob_psys[psys.settings.original] = rman_sg_particles self.rman_scene.rman_particles[ob.original] = ob_psys - proto_key = o.original.data.original + proto_key = object_utils.prototype_key(ob) rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if rman_sg_node: if not rman_sg_node.rman_sg_particle_group_node: From 89a404a1616e3d21aceb1db22ed650bdef682e87 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 15:28:14 -0800 Subject: [PATCH 015/278] Make sure we didn't particle systems if the object is deleted. --- rman_scene.py | 6 +++--- rman_scene_sync.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 7ae66fd2..b9cb3862 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -762,9 +762,9 @@ def export_data_block(self, proto_key, ob, db): psys_translator.set_motion_steps(rman_sg_particles, subframes) psys_translator.update(ob, psys, rman_sg_particles) - ob_psys = self.rman_particles.get(ob.original, dict()) + ob_psys = self.rman_particles.get(proto_key, dict()) ob_psys[psys.settings.original] = rman_sg_particles - self.rman_particles[ob.original] = ob_psys + self.rman_particles[proto_key] = ob_psys rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) elif rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): # Make sure empties that are hidden still go out. Children @@ -837,7 +837,7 @@ def export_instances_motion(self, selected_objects=list()): # check particles for motion for psys in ob.particle_systems: - ob_psys = self.rman_particles.get(ob.original, None) + ob_psys = self.rman_particles.get(proto_key, None) if not ob_psys: continue rman_sg_particles = ob_psys.get(psys.settings.original, None) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index f4af9f3a..903fea4b 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -779,13 +779,13 @@ def update_scene(self, context, depsgraph): self.rman_updates[col_obj.original] = rman_update else: psys_db_name = '%s' % psys.name - ob_psys = self.rman_scene.rman_particles.get(ob.original, dict()) + proto_key = object_utils.prototype_key(ob) + ob_psys = self.rman_scene.rman_particles.get(proto_key, dict()) rman_sg_particles = ob_psys.get(obj.id.original, None) if not rman_sg_particles: rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) ob_psys[psys.settings.original] = rman_sg_particles - self.rman_scene.rman_particles[ob.original] = ob_psys - proto_key = object_utils.prototype_key(ob) + self.rman_scene.rman_particles[proto_key] = ob_psys rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if rman_sg_node: if not rman_sg_node.rman_sg_particle_group_node: @@ -1176,6 +1176,13 @@ def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) self.clear_instances(None, rman_sg_node) + if key in self.rman_scene.rman_particles: + rfb_log().debug("\t\tRemoving particles...") + ob_psys = self.rman_scene.rman_particles[key] + for k, v in ob_psys.items(): + self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) + del self.rman_scene.rman_particles[key] + self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_node.sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) From 6ad5f4b451caacbf51dd716d7ccc19ca30b0bb93 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 16:24:29 -0800 Subject: [PATCH 016/278] Make sure to remove rman_sg_particle nodes that are no longer in use. --- rman_scene_sync.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 903fea4b..da19832f 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -1159,6 +1159,23 @@ def update_scene(self, context, depsgraph): if (len(ob_eval.particle_systems) > 0) and instance.show_particles: rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + # Delete any removed partcle systems + if proto_key in self.rman_scene.rman_particles: + ob_psys = self.rman_scene.rman_particles[proto_key] + rman_particle_nodes = list(ob_psys) + for psys in ob_eval.particle_systems: + try: + rman_particle_nodes.remove(psys.settings.original) + except: + continue + if rman_particle_nodes: + rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) + for k in rman_particle_nodes: + rman_particles_node = ob_psys[k] + self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) + del ob_psys[k] + + # delete stuff if deleted_obj_keys: self.delete_objects(deleted_obj_keys) From 2a7e398150c0efeda059b6e13f8ca637e047c533 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 16:37:37 -0800 Subject: [PATCH 017/278] Remove the check for local_view. This is not needed now that we're using context.visible_objects --- rman_scene.py | 6 ------ rman_scene_sync.py | 27 --------------------------- 2 files changed, 33 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index b9cb3862..9bd17e5a 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -137,7 +137,6 @@ def __init__(self, rman_render=None): self.num_object_instances = 0 self.num_objects_in_viewlayer = 0 self.objects_in_viewlayer = list() - self.bl_local_view = False self.create_translators() @@ -216,7 +215,6 @@ def export_for_final_render(self, depsgraph, sg_scene, bl_view_layer, is_externa self.external_render = is_external self.is_interactive = False self.is_viewport_render = False - self.bl_local_view = False self.do_motion_blur = self.bl_scene.renderman.motion_blur self.export() @@ -232,7 +230,6 @@ def export_for_bake_render(self, depsgraph, sg_scene, bl_view_layer, is_external self.is_viewport_render = False self.do_motion_blur = self.bl_scene.renderman.motion_blur self.rman_bake = True - self.bl_local_view = False if self.bl_scene.renderman.hider_type == 'BAKE_BRICKMAP_SELECTED': self.export_bake_brickmap_selected() @@ -245,7 +242,6 @@ def export_for_interactive_render(self, context, depsgraph, sg_scene): self.bl_view_layer = context.view_layer self.bl_scene = depsgraph.scene_eval self._find_renderman_layer() - self.bl_local_view = context.space_data.local_view self.depsgraph = depsgraph self.external_render = False self.is_interactive = True @@ -262,7 +258,6 @@ def export_for_interactive_render(self, context, depsgraph, sg_scene): def export_for_rib_selection(self, context, sg_scene): self.reset() self.bl_scene = context.scene - self.bl_local_view = False self.bl_frame_current = self.bl_scene.frame_current self.sg_scene = sg_scene self.context = context @@ -280,7 +275,6 @@ def export_for_rib_selection(self, context, sg_scene): def export_for_swatch_render(self, depsgraph, sg_scene): self.sg_scene = sg_scene self.context = bpy.context #None - self.bl_local_view = False self.bl_scene = depsgraph.scene_eval self.depsgraph = depsgraph self.external_render = False diff --git a/rman_scene_sync.py b/rman_scene_sync.py index da19832f..be78e887 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -73,33 +73,6 @@ def update_view(self, context, depsgraph): translator.update_transform(camera, rman_sg_camera) def _scene_updated(self): - - # Check changes to local view - if self.rman_scene.bl_local_view and (self.rman_scene.context.space_data.local_view is None): - self.rman_scene.bl_local_view = False - for ob in self.rman_scene.bl_scene.objects: - if ob.type in ('ARMATURE', 'CURVE', 'CAMERA', 'LIGHT'): - continue - if ob.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob.original] = rman_update - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.check_solo_light() - elif not self.rman_scene.bl_local_view and (self.rman_scene.context.space_data.local_view is not None): - self.rman_scene.bl_local_view = True - for ob in self.rman_scene.bl_scene.objects: - if ob.type in ('ARMATURE', 'CURVE', 'CAMERA', 'LIGHT'): - continue - if ob.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob.original] = rman_update - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.check_solo_light() - # Check visible objects visible_objects = self.rman_scene.context.visible_objects if not self.num_instances_changed: From c23776dc0d1d39ba281b3d2239bd8c203d4c05e6 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 4 Feb 2022 16:59:13 -0800 Subject: [PATCH 018/278] Use prototype_key function to get a key for lightfilters. --- rman_operators/rman_operators_collections.py | 5 ++++- rman_scene_sync.py | 16 +++++++--------- rman_translators/rman_lightfilter_translator.py | 4 +++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/rman_operators/rman_operators_collections.py b/rman_operators/rman_operators_collections.py index 907e1dbc..6f3ff34a 100644 --- a/rman_operators/rman_operators_collections.py +++ b/rman_operators/rman_operators_collections.py @@ -229,7 +229,10 @@ def invoke(self, context, event): collection.add() index += 1 setattr(rm, coll_idx, index) - collection[-1].name = dflt_name + try: + collection[-1].name = dflt_name + except: + pass elif self.properties.action == 'REMOVE': collection.remove(index) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index be78e887..04fb1471 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -179,17 +179,19 @@ def _material_updated(self, obj): def _light_filter_transform_updated(self, obj): ob = obj.id.evaluated_get(self.rman_scene.depsgraph) - rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(ob.original.data.original, None) + proto_key = object_utils.prototype_key(ob) + rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(proto_key, None) if rman_sg_lightfilter: rman_group_translator = self.rman_scene.rman_translators['GROUP'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_group_translator.update_transform(ob, rman_sg_lightfilter) def light_filter_updated(self, obj): - rman_sg_node = self.rman_scene.rman_prototypes.get(obj.id.original.data.original, None) + ob = obj.id.evaluated_get(self.rman_scene.depsgraph) + proto_key = object_utils.prototype_key(ob) + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if not rman_sg_node: return - ob = obj.id if obj.is_updated_transform or obj.is_updated_shading: rfb_log().debug("\tLight Filter: %s Transform Updated" % obj.id.name) self._light_filter_transform_updated(obj) @@ -199,13 +201,9 @@ def light_filter_updated(self, obj): self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) for light_ob in rman_sg_node.lights_list: if isinstance(light_ob, bpy.types.Material): - rman_sg_material = self.rman_scene.rman_materials.get(light_ob.original, None) - if rman_sg_material: - self.rman_scene.rman_translators['MATERIAL'].update_light_filters(light_ob, rman_sg_material) + light_ob.node_tree.update_tag() else: - rman_sg_light = self.rman_scene.rman_prototypes.get(light_ob.original.data.original, None) - if rman_sg_light: - self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) + light_ob.update_tag() def camera_updated(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) diff --git a/rman_translators/rman_lightfilter_translator.py b/rman_translators/rman_lightfilter_translator.py index 9e2d80cf..4a7483f5 100644 --- a/rman_translators/rman_lightfilter_translator.py +++ b/rman_translators/rman_lightfilter_translator.py @@ -88,12 +88,14 @@ def export(self, ob, db_name): rman_sg_lightfilter = RmanSgLightFilter(self.rman_scene, sg_group, db_name) rman_sg_lightfilter.sg_filter_node = sg_filter_node rman_sg_lightfilter.coord_sys = db_name + rman_sg_lightfilter.rman_type = 'LIGHTFILTER' rman_group_translator = self.rman_scene.rman_translators['GROUP'] rman_group_translator.update_transform(ob, rman_sg_lightfilter) self.rman_scene.get_root_sg_node().AddChild(rman_sg_lightfilter.sg_node) - self.rman_scene.rman_prototypes[ob.original.data.original] = rman_sg_lightfilter + proto_key = object_utils.prototype_key(ob) + self.rman_scene.rman_prototypes[proto_key] = rman_sg_lightfilter self.rman_scene.sg_scene.Root().AddCoordinateSystem(rman_sg_lightfilter.sg_node) return rman_sg_lightfilter From 0b02913d25a7787f190add0d0e2f1e9f12e8b804 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 10:09:41 -0800 Subject: [PATCH 019/278] Fix problem attaching a new material to an empty. Also, remove unncessary code. --- rfb_utils/object_utils.py | 22 +- rman_scene.py | 30 +-- rman_scene_sync.py | 538 +++++--------------------------------- 3 files changed, 91 insertions(+), 499 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index d44d3527..aa2e5669 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -141,19 +141,15 @@ def is_transforming(ob, recurse=False): transforming = ob.parent.data.use_path return transforming -def detect_id_type(db, ob=None): - if isinstance(db, bpy.types.Mesh): - return 'MESH' - elif isinstance(db, bpy.types.Light): - if db.renderman.renderman_light_role == 'RMAN_LIGHTFILTER': - return 'LIGHTFILTER' - return 'LIGHT' - elif isinstance(db, bpy.types.Volume): - return 'OPENVDB' - elif ob: - return _detect_primitive_(ob) - - return None +def has_empty_parent(ob): + # check if the parent of ob is an Empty + if not ob: + return False + if not ob.parent: + return False + if _detect_primitive_(ob.parent) == 'EMPTY': + return True + return False def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): diff --git a/rman_scene.py b/rman_scene.py index 9bd17e5a..7474d06b 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -610,12 +610,7 @@ def export_data_blocks(self, selected_objects=list()): ob_eval = ob.evaluated_get(self.depsgraph) psys = None parent = None - proto_key = object_utils.prototype_key(ob_inst) - if ob.type == 'EMPTY': - ob_data = None - else: - ob_data = ob_eval.data.original - + proto_key = object_utils.prototype_key(ob_inst) if ob_inst.is_instance: psys = ob_inst.particle_system parent = ob_inst.parent @@ -624,7 +619,7 @@ def export_data_blocks(self, selected_objects=list()): continue rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.export_data_block(proto_key, ob_eval, ob_data) + rman_sg_node = self.export_data_block(proto_key, ob_eval) if not rman_sg_node: continue @@ -657,13 +652,13 @@ def export_data_blocks(self, selected_objects=list()): else: self.attach_material(ob, rman_sg_group) - if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + if object_utils.has_empty_parent(ob): # this object is a child of an empty. Add it to the empty. - rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) + parent_proto_key = object_utils.prototype_key(ob.parent) + rman_empty_node = self.rman_prototypes.get(parent_proto_key, None) if not rman_empty_node: - key = ob.parent.original ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) - rman_empty_node = self.export_data_block(key, ob_parent_eval, None) + rman_empty_node = self.export_data_block(parent_proto_key, ob_parent_eval) self._export_hidden_instance(ob_parent_eval, rman_empty_node) if not rman_empty_node: @@ -682,9 +677,8 @@ def export_data_blocks(self, selected_objects=list()): translator.export_object_id(ob, rman_sg_group, ob_inst) - def export_data_block(self, proto_key, ob, db): - rman_type = object_utils.detect_id_type(db, ob=ob) - + def export_data_block(self, proto_key, ob): + rman_type = object_utils._detect_primitive_(ob) if proto_key in self.rman_prototypes: return self.rman_prototypes[proto_key] @@ -1027,12 +1021,12 @@ def _export_hidden_instance(self, ob, rman_sg_node): translator = self.rman_translators.get('EMPTY') translator.export_object_attributes(ob, rman_sg_node) self.attach_material(ob, rman_sg_node) - if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + if object_utils.has_empty_parent(ob): + parent_proto_key = object_utils.prototype_key(ob.parent) rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) if not rman_empty_node: # Empty was not created. Export it. - key = ob.parent.original - rman_empty_node = self.export_data_block(key, ob.parent, None) + rman_empty_node = self.export_data_block(parent_proto_key, ob.parent, None) if not rman_empty_node: self.get_root_sg_node().AddChild(rman_sg_node.sg_node) translator.export_transform(ob, rman_sg_node.sg_node) @@ -1219,7 +1213,7 @@ def attach_material(self, ob, rman_sg_node): mat = object_utils.get_active_material(ob) if mat: rman_sg_material = self.rman_materials.get(mat.original, None) - if rman_sg_material and rman_sg_material.sg_node: + if rman_sg_material and rman_sg_material.sg_node: scenegraph_utils.set_material(rman_sg_node.sg_node, rman_sg_material.sg_node) rman_sg_node.is_meshlight = rman_sg_material.has_meshlight diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 04fb1471..45a28d6d 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -36,17 +36,10 @@ def __init__(self, rman_render=None, rman_scene=None, sg_scene=None): self.rman = rman_render.rman self.rman_scene = rman_scene self.sg_scene = sg_scene - - self.new_objects = set() # set of objects that were added to the scene - self.new_cameras = set() # set of new camera objects that were added to the scene - self.update_instances = set() # set of objects we need to update their instances - self.update_particles = set() # set of objects we need to update their particle systemd - self.do_delete = False # whether or not we need to do an object deletion - self.do_add = False # whether or not we need to add an object self.num_instances_changed = False # if the number of instances has changed since the last update self.frame_number_changed = False - self.rman_updates = dict() + self.rman_updates = dict() # A dicitonary to hold RmanUpdate instances @property def sg_scene(self): @@ -72,7 +65,7 @@ def update_view(self, context, depsgraph): else: translator.update_transform(camera, rman_sg_camera) - def _scene_updated(self): + def scene_updated(self): # Check visible objects visible_objects = self.rman_scene.context.visible_objects if not self.num_instances_changed: @@ -154,7 +147,7 @@ def _mesh_light_update(self, mat): for ob in object_list: ob.update_tag() - def _material_updated(self, obj): + def material_updated(self, obj): mat = obj.id rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) translator = self.rman_scene.rman_translators["MATERIAL"] @@ -177,7 +170,7 @@ def _material_updated(self, obj): # update db_name rman_sg_material.db_name = db_name - def _light_filter_transform_updated(self, obj): + def light_filter_transform_updated(self, obj): ob = obj.id.evaluated_get(self.rman_scene.depsgraph) proto_key = object_utils.prototype_key(ob) rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(proto_key, None) @@ -194,7 +187,7 @@ def light_filter_updated(self, obj): return if obj.is_updated_transform or obj.is_updated_shading: rfb_log().debug("\tLight Filter: %s Transform Updated" % obj.id.name) - self._light_filter_transform_updated(obj) + self.light_filter_transform_updated(obj) if obj.is_updated_geometry: rfb_log().debug("\tLight Filter: %s Shading Updated" % obj.id.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -234,76 +227,7 @@ def camera_updated(self, ob_update): return rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) translator._update_render_cam_transform(ob, rman_sg_camera) - - def _gpencil_transform_updated(self, obj): - ob = obj.id - rman_sg_gpencil = self.rman_scene.rman_objects.get(ob.original, None) - if rman_sg_gpencil: - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - rman_group_translator = self.rman_scene.rman_translators['GROUP'] - for ob_inst in self.rman_scene.depsgraph.object_instances: - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_sg_gpencil.instances.get(group_db_name, None) - if rman_sg_group: - rman_group_translator.update_transform(ob, rman_sg_group) - - def _obj_geometry_updated(self, obj): - ob = obj.id - rman_type = object_utils._detect_primitive_(ob) - db_name = object_utils.get_db_name(ob, rman_type=rman_type) - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) - - if rman_type in ['LIGHT', 'LIGHTFILTER', 'CAMERA']: - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - if rman_type == 'LIGHTFILTER': - self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) - for light_ob in rman_sg_node.lights_list: - if isinstance(light_ob, bpy.types.Material): - rman_sg_material = self.rman_scene.rman_materials.get(light_ob.original, None) - if rman_sg_material: - self.rman_scene.rman_translators['MATERIAL'].update_light_filters(light_ob, rman_sg_material) - else: - rman_sg_light = self.rman_scene.rman_objects.get(light_ob.original, None) - if rman_sg_light: - self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) - - elif rman_type == 'LIGHT': - self.rman_scene.rman_translators['LIGHT'].update(ob, rman_sg_node) - - if not self.rman_scene.scene_solo_light: - # only set if a solo light hasn't been set - if not self.rman_scene.check_light_local_view(ob, rman_sg_node): - rman_sg_node.sg_node.SetHidden(ob.data.renderman.mute) - elif rman_type == 'CAMERA': - ob = ob.original - rman_camera_translator = self.rman_scene.rman_translators['CAMERA'] - if not self.rman_scene.is_viewport_render: - rman_camera_translator.update(ob, rman_sg_node) - else: - rman_camera_translator.update_viewport_cam(ob, rman_sg_node, force_update=True) - - else: - if rman_sg_node.rman_type != rman_type: - # for now, we don't allow the rman_type to be changed - rfb_log().error("Changing primitive type is currently not supported.") - return - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator = self.rman_scene.rman_translators.get(rman_type, None) - if not translator: - return - translator.update(ob, rman_sg_node) - translator.export_object_primvars(ob, rman_sg_node) - # material slots could have changed, so we need to double - # check that too - for k,v in rman_sg_node.instances.items(): - self.rman_scene.attach_material(ob, v) - - if rman_sg_node.sg_node: - if not ob.show_instancer_for_viewport: - rman_sg_node.sg_node.SetHidden(1) - else: - rman_sg_node.sg_node.SetHidden(-1) - + def update_light_visibility(self, rman_sg_node, ob): if not self.rman_scene.scene_solo_light: rman_sg_node.sg_node.SetHidden(ob.renderman.mute) @@ -316,101 +240,6 @@ def update_light_visibility(self, rman_sg_node, ob): else: rman_sg_node.sg_node.SetHidden(1) - - ''' - vis = rman_sg_node.sg_node.GetHidden() - if vis == -1: - vis = 0 - result = False - update_instances = False - # if vis is inherit, and none of the other visibility attrs are set to hide - if vis == -1 and not ob.hide_get() and int(ob.renderman.mute) == 0: - update_instances = True - result = False - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - if self.rman_scene.check_light_local_view(ob, rman_sg_node): - update_instances = True - result = True - elif not ob.hide_get(): - rman_sg_node.sg_node.SetHidden(ob.renderman.mute) - update_instances = True - result = (vis != int(ob.renderman.mute)) - else: - rman_sg_node.sg_node.SetHidden(1) - result = (vis != 1) - - if update_instances and len(rman_sg_node.instances) < 1: - self.update_instances.add(ob.original) - return result - ''' - - def update_object_visibility(self, rman_sg_node, ob): - ob_data = bpy.data.objects.get(ob.name, ob) - rman_type = object_utils._detect_primitive_(ob_data) - particle_systems = getattr(ob_data, 'particle_systems', list()) - has_particle_systems = len(particle_systems) > 0 - is_hidden = ob_data.hide_get() - - # double check hidden value - if rman_type in ['LIGHT']: - if self.update_light_visibility(rman_sg_node, ob): - rfb_log().debug("Update light visibility: %s" % ob.name) - return True - else: - if rman_sg_node.is_hidden != is_hidden: - self.do_delete = False - rman_sg_node.is_hidden = is_hidden - if rman_type == 'EMPTY': - self.update_empty(ob, rman_sg_node) - else: - self.update_instances.add(ob.original) - self.clear_instances(ob, rman_sg_node) - if has_particle_systems: - self.update_particles.add(ob.original) - return True - return False - - def update_particle_settings(self, obj, particle_settings_node=None): - rfb_log().debug("Check %s for particle settings." % obj.id.name) - # A ParticleSettings node was updated. Try to look for it. - ob = obj.id - rman_type = object_utils._detect_primitive_(ob) - for psys in obj.id.particle_systems: - if psys.settings.original == particle_settings_node: - if psys.settings.type == 'FLIP' and rman_type == 'FLUID': - fluid_translator = self.rman_scene.rman_translators['FLUID'] - rman_sg_node = self.rman_scene.rman_objects.get(ob.original, None) - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - fluid_translator.update(ob, rman_sg_node) - return - - ob_psys = self.rman_scene.rman_particles.get(obj.id.original, dict()) - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if rman_sg_particles: - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - psys_translator = self.rman_scene.rman_translators['PARTICLES'] - psys_translator.update(obj.id, psys, rman_sg_particles) - return - # This is a particle instancer. The instanced object needs to updated - elif object_utils.is_particle_instancer(psys): - inst_object = getattr(particle_settings_node, 'instance_object', None) - collection = getattr(particle_settings_node, 'instance_collection', None) - if inst_object: - self.update_instances.add(inst_object.original) - if collection: - for col_obj in collection.all_objects: - if col_obj.original not in self.rman_scene.rman_objects: - self.new_objects.add(col_obj.original) - self.update_instances.add(col_obj.original) - break - - # Update any other instance objects this object instanced. The instanced - # object may have changed - rman_sg_node = self.rman_scene.rman_objects.get(obj.id.original, None) - for instance_obj in rman_sg_node.objects_instanced: - self.clear_instances(instance_obj) - self.update_instances.add(instance_obj) - def check_particle_systems(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) for psys in ob.particle_systems: @@ -457,48 +286,23 @@ def update_empty(self, ob_update, rman_sg_node=None): self.rman_updates[col_obj.original] = rman_update else: rfb_log().debug("\tRegular empty") - rman_sg_node = self.rman_scene.rman_prototypes.get(ob.original, None) + proto_key = object_utils.prototype_key(ob) + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if not rman_sg_node: return - translator = self.rman_scene.rman_translators['EMPTY'] + translator = self.rman_scene.rman_translators['EMPTY'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): translator.export_transform(ob, rman_sg_node.sg_node) + translator.export_object_attributes(ob, rman_sg_node) + self.rman_scene.attach_material(ob, rman_sg_node) if ob.renderman.export_as_coordsys: self.rman_scene.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) else: self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) - ''' - if ob.is_instancer: - collection = ob.instance_collection - if collection: - if self.num_instances_changed: - for col_obj in collection.all_objects: - self.update_instances.add(col_obj.original) - rman_instance_sg_node = self.rman_scene.rman_objects.get(col_obj.original, None) - if rman_instance_sg_node: - self.clear_instances(col_obj.original, rman_instance_sg_node) - else: - self.new_objects.add(col_obj.original) - self.update_particles.add(col_obj) - else: - for col_obj in collection.all_objects: - self.update_instances.add(col_obj.original) - self.update_particles.add(col_obj) - - else: - translator = self.rman_scene.rman_translators['EMPTY'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator.export_transform(ob, rman_sg_node.sg_node) - if ob.renderman.export_as_coordsys: - self.rman_scene.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) - else: - self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) - ''' - def clear_instances(self, ob, rman_sg_node=None): - for k,rman_sg_group in rman_sg_node.instances.items(): - if ob and ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': + for k,rman_sg_group in rman_sg_node.instances.items(): + if object_utils.has_empty_parent(ob): proto_key = object_utils.prototype_key(ob.parent) rman_empty_node = self.rman_scene.rman_prototypes.get(proto_key) if rman_empty_node: @@ -509,7 +313,11 @@ def clear_instances(self, ob, rman_sg_node=None): rman_sg_node.instances.clear() def update_materials_dict(self, mat): - # See comment below in update_objects_dict + # Try to see if we already have a material with the same db_name + # We need to do this because undo/redo causes all bpy.types.ID + # references to be invalidated (see: https://docs.blender.org/api/current/info_gotcha.html) + # We don't want to accidentally mistake this for a new object, so we need to update + # our materials dictionary with the new bpy.types.ID reference rman_sg_material = None for id, rman_sg_node in self.rman_scene.rman_materials.items(): if rman_sg_node: @@ -522,26 +330,6 @@ def update_materials_dict(self, mat): return rman_sg_material - def update_objects_dict(self, ob, rman_type=None): - # Try to see if we already have an obj with the same db_name - # We need to do this because undo/redo causes all bpy.types.ID - # references to be invalidated (see: https://docs.blender.org/api/current/info_gotcha.html) - # We don't want to accidentally mistake this for a new object, so we need to update - # our objects dictionary with the new bpy.types.ID reference - rman_sg_node = None - for id, rsn in self.rman_scene.rman_objects.items(): - if rsn: - db_name = object_utils.get_db_name(ob, rman_type=rman_type) - if rsn.db_name == db_name: - self.rman_scene.rman_objects[ob.original] = rsn - del self.rman_scene.rman_objects[id] - if id in self.rman_scene.rman_cameras: - self.rman_scene.rman_cameras[ob.original] = rsn - del self.rman_scene.rman_cameras[id] - rman_sg_node = rsn - break - return rman_sg_node - def update_collection(self, coll): # mark all objects in a collection # as needing their instances updated @@ -618,14 +406,7 @@ def update_portals(self, ob): def update_scene(self, context, depsgraph): ## FIXME: this function is waaayyy too big and is doing too much stuff - self.new_objects.clear() - self.new_cameras.clear() - self.update_instances.clear() - self.update_particles.clear() self.rman_updates = dict() - - self.do_delete = False # whether or not we need to do an object deletion - self.do_add = False # whether or not we need to add an object self.num_instances_changed = False # if the number of instances has changed since the last update self.frame_number_changed = False @@ -633,18 +414,12 @@ def update_scene(self, context, depsgraph): self.rman_scene.bl_scene = depsgraph.scene self.rman_scene.context = context - particle_settings_node = None did_mesh_update = False # did the mesh actually update - prev_num_instances = self.rman_scene.num_object_instances # the number of instances previously # Check the number of instances. If we differ, an object may have been # added or deleted if self.rman_scene.num_object_instances != len(depsgraph.object_instances): self.num_instances_changed = True - if self.rman_scene.num_object_instances > len(depsgraph.object_instances): - self.do_delete = True - else: - self.do_add = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) rfb_log().debug("------Start update scene--------") @@ -652,7 +427,7 @@ def update_scene(self, context, depsgraph): ob = obj.id if isinstance(obj.id, bpy.types.Scene): - self._scene_updated() + self.scene_updated() elif isinstance(obj.id, bpy.types.World): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -680,7 +455,7 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.Material): rfb_log().debug("Material updated: %s" % obj.id.name) - self._material_updated(obj) + self.material_updated(obj) elif isinstance(obj.id, bpy.types.Mesh): rfb_log().debug("Mesh updated: %s" % obj.id.name) @@ -711,9 +486,6 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.ParticleSettings): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) - # Save this particle settings node, so we can check for it later - # when we process object changes - #particle_settings_node = obj.id.original users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -826,155 +598,7 @@ def update_scene(self, context, depsgraph): translator.update(cam_object, rman_sg_camera) - continue - - - particle_systems = getattr(obj.id, 'particle_systems', list()) - has_particle_systems = len(particle_systems) > 0 - - rman_type = object_utils._detect_primitive_(ob) - # grab the object from bpy.data, because the depsgraph doesn't seem - # to get the updated viewport hidden value - ob_data = bpy.data.objects.get(ob.name, ob) - rman_sg_node = self.rman_scene.rman_objects.get(obj.id.original, None) - - # NOTE:hide _get() and hide_viewport are two different things in Blender - # hide_get() hides the object from the viewport, but it does not actually remove the object - # as instances of the object can still be visible (ex: in particle systems) - # hide_viewport should be interpreted as an actual deleted object, including - # particle instances. - is_hidden = ob_data.hide_get() - - if not rman_sg_node: - rman_sg_node = self.update_objects_dict(obj.id, rman_type=rman_type) - - if self.do_add and not rman_sg_node: - rman_type = object_utils._detect_primitive_(ob_data) - - if ob_data.hide_get(): - # don't add if this hidden in the viewport - continue - if ob.type == 'CAMERA': - self.new_cameras.add(obj.id.original) - else: - if rman_type == 'EMPTY' and ob.is_instancer: - self.update_empty(ob) - else: - if rman_type == 'LIGHT': - # double check if this light is an rman light - # for now, we don't support adding Blender lights in IPR - # - # we can also get to this point when adding new rman lights because - # blender will tell us a new light has been added before we've had to chance - # to modify its properties to be an rman light, so we don't want to - # add this light just yet. - if not shadergraph_utils.is_rman_light(ob): - self.rman_scene.num_object_instances = prev_num_instances - rfb_log().debug("------End update scene----------") - return - elif rman_type == 'EMPTY': - # same issue can also happen with empty - # we have not been able to tag our types before Blender - # tells us an empty has been added - self.rman_scene.num_object_instances = prev_num_instances - rfb_log().debug("------End update scene----------") - return - rfb_log().debug("New object added: %s" % obj.id.name) - self.new_objects.add(obj.id.original) - self.update_instances.add(obj.id.original) - if rman_type == 'LIGHTFILTER': - # Add Light filters immediately, so that lights - # can reference them ASAP. - self.add_objects() - self.new_objects.remove(obj.id.original) - self.num_instances_changed = False - continue - - if rman_sg_node and rman_sg_node.sg_node: - # update db_name - db_name = object_utils.get_db_name(ob, rman_type=rman_type) - rman_sg_node.db_name = db_name - if self.update_object_visibility(rman_sg_node, ob): - continue - else: - continue - - if obj.is_updated_transform: - rfb_log().debug("Transform updated: %s" % obj.id.name) - if ob.type in ['CAMERA']: - # we deal with main camera transforms in view_draw - rman_sg_camera = self.rman_scene.rman_cameras[ob.original] - if rman_sg_camera == self.rman_scene.main_camera: - continue - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator._update_render_cam_transform(ob, rman_sg_camera) - continue - - if rman_type == 'LIGHTFILTER': - self._light_filter_transform_updated(obj) - elif rman_type == 'GPENCIL': - # FIXME: we shouldn't handle this specifically, but we seem to be - # hitting a prman crash when removing and adding instances of - # grease pencil curves - self._gpencil_transform_updated(obj) - elif rman_type == 'EMPTY': - self.update_empty(ob, rman_sg_node) - elif self.num_instances_changed: - rman_sg_node = self.rman_scene.rman_objects.get(obj.id.original, None) - for instance_obj in rman_sg_node.objects_instanced: - self.clear_instances(instance_obj) - self.update_instances.add(instance_obj) - rman_sg_node.objects_instanced.clear() - else: - # This is a simple transform. We don't clear the instances. - - # We always have to update particle systems when the object has transformed - # A transform changed can also be triggered when a particle system is removed. - self.update_particles.add(obj.id) - self.update_instances.add(obj.id.original) - self.update_geometry_node_instances(obj.id) - self.do_delete = False - if rman_type == 'LIGHT': - # check if portals are attached - self.update_portals(obj.id.original) - - # Check if this object is the focus object the camera. If it is - # we need to update the camera - rman_sg_camera = self.rman_scene.main_camera - if rman_sg_camera.rman_focus_object and rman_sg_camera.rman_focus_object == rman_sg_node: - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - cam_object = translator.find_scene_camera() - translator.update(cam_object, rman_sg_camera) - - if obj.is_updated_geometry: - if is_hidden: - # don't update if this is hidden - continue - - rfb_log().debug("Object updated: %s" % obj.id.name) - if has_particle_systems and particle_settings_node: - self.do_delete = False - self.update_particle_settings(obj, particle_settings_node) - else: - # We always update particle systems in the non-num_instance_change case - # because the particle system can be pointing to a whole new particle settings - self.update_particles.add(obj.id) - - if not self.num_instances_changed: - if rman_type == 'MESH' and not did_mesh_update: - # if a mesh didn't actually update don't call obj_geometry_updated - rfb_log().debug("Skip object updated: %s" % obj.id.name) - continue - self._obj_geometry_updated(obj) - elif isinstance(obj.id, bpy.types.Collection): - # don't check the collection if we know objects - # were added or deleted in the scene. - if self.do_delete or self.do_add: - continue - rfb_log().debug("Collection updated: %s" % obj.id.name) #self.update_collection(obj.id) @@ -1009,9 +633,6 @@ def update_scene(self, context, depsgraph): if proto_key in deleted_obj_keys: deleted_obj_keys.remove(proto_key) - db = None - if instance.object.data: - db = instance.object.data rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) @@ -1021,6 +642,7 @@ def update_scene(self, context, depsgraph): # This can happen because # we have not been able to tag our types before Blender # tells us an object has been added + # For now, just delete the existing sg_node rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key) self.clear_instances(ob_eval, rman_sg_node) self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) @@ -1033,11 +655,12 @@ def update_scene(self, context, depsgraph): # We don't support adding new Blender lights if not shadergraph_utils.is_rman_light(ob_eval): continue - rfb_log().debug("\tAdding: %s" % proto_key) - rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval, db) + rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval) if not rman_sg_node: continue + rfb_log().debug("\tNew Object added: %s (%s)" % (proto_key, rman_type)) + if rman_type == 'LIGHTFILTER': # update all lights with this light filter users = bpy.context.blend_data.user_map(subset={ob_eval.original}) @@ -1055,7 +678,16 @@ def update_scene(self, context, depsgraph): self.rman_updates[ob_key] = rman_update rman_update.is_updated_geometry = False - if not self.num_instances_changed: + if self.num_instances_changed: + # If the number of instances has changed, + # we check all instances in the scene + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + else: if ob_key not in self.rman_updates: if not parent: continue @@ -1071,13 +703,6 @@ def update_scene(self, context, depsgraph): self.rman_updates[ob_key] = rman_update else: rman_update = self.rman_updates[ob_key] - else: - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update if rman_sg_node and not is_new_object: if rman_update.is_updated_geometry and proto_key not in already_udpated: @@ -1086,35 +711,63 @@ def update_scene(self, context, depsgraph): translator.update(ob_eval, rman_sg_node) already_udpated.append(proto_key) - if rman_type == 'LIGHTFILTER': - # Light filters are special + if rman_type == 'EMPTY': + self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) + continue + + elif rman_type == 'LIGHTFILTER': + # Light filters are special. We don't need to add instances + # of them, as they are part of lights continue if rman_sg_node not in clear_instances: - # clear all instances + # clear all instances for this prototype, if + # we have not already done so rfb_log().debug("\tClearing instances: %s" % proto_key) self.clear_instances(ob_eval, rman_sg_node) clear_instances.append(rman_sg_node) if not self.rman_scene.check_visibility(instance): + # This instance is not visible in the viewport. Don't + # add an instance of it continue - if rman_type == 'LIGHT': - self.rman_scene.check_solo_light(rman_sg_node, ob_eval) - group_db_name = object_utils.get_group_db_name(instance) rman_sg_group = rman_sg_node.instances.get(group_db_name, None) if not rman_sg_group: rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) rman_sg_node.instances[group_db_name] = rman_sg_group - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + if object_utils.has_empty_parent(ob_eval): + # this object is a child of an empty. Add it to the empty. + parent_proto_key = object_utils.prototype_key(ob_eval.parent) + rman_empty_node = self.rman_scene.rman_prototypes.get(parent_proto_key, None) + if not rman_empty_node: + ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) + rman_empty_node = self.rman_scene.export_data_block(parent_proto_key, ob_parent_eval) + self.rman_scene._export_hidden_instance(ob_parent_eval, rman_empty_node) + if parent_proto_key in deleted_obj_keys: + deleted_obj_keys.remove(parent_proto_key) + + if not rman_empty_node: + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + else: + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + else: + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) rman_group_translator.update_transform(instance, rman_sg_group) if rman_type == 'LIGHT': + # We are dealing with a light. Check if it's a solo light, or muted + self.rman_scene.check_solo_light(rman_sg_node, ob_eval) + + # Hide the default light if self.rman_scene.default_light.GetHidden() != 1: self.rman_scene.default_light.SetHidden(1) + continue # Attach a material @@ -1146,8 +799,7 @@ def update_scene(self, context, depsgraph): self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) del ob_psys[k] - - # delete stuff + # delete objects if deleted_obj_keys: self.delete_objects(deleted_obj_keys) @@ -1193,56 +845,6 @@ def delete_objects(self, deleted_obj_keys=list()): if not self.rman_scene.scene_any_lights: self.rman_scene.default_light.SetHidden(0) - ''' - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - keys = [k for k in self.rman_scene.rman_objects.keys()] - for obj in keys: - try: - ob = self.rman_scene.bl_scene.objects.get(obj.name_full, None) - # NOTE: objects that are hidden from the viewport are considered deleted - # objects as well - if ob and not ob.hide_viewport: - rman_sg_node = self.rman_scene.rman_objects.get(obj, None) - if rman_sg_node: - # Double check object visibility - self.update_object_visibility(rman_sg_node, ob) - continue - except Exception as e: - pass - - rman_sg_node = self.rman_scene.rman_objects.get(obj, None) - if rman_sg_node: - for k,v in rman_sg_node.instances.items(): - if v.sg_node: - self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) - rman_sg_node.instances.clear() - - # For now, don't delete the geometry itself - # there may be a collection instance still referencing the geo - - # self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) - del self.rman_scene.rman_objects[obj] - - # We just deleted a light filter. We need to tell all lights - # associated with this light filter to update - if isinstance(rman_sg_node, RmanSgLightFilter): - for light_ob in rman_sg_node.lights_list: - light_key = object_utils.get_db_name(light_ob, rman_type='LIGHT') - rman_sg_light = self.rman_scene.rman_objects.get(light_ob.original, None) - if rman_sg_light: - self.rman_scene.rman_translators['LIGHT'].update_light_filters(light_ob, rman_sg_light) - try: - self.rman_scene.processed_obs.remove(obj) - except ValueError: - rfb_log().debug("Obj not in self.rman_scene.processed_obs: %s") - pass - - if self.rman_scene.render_default_light: - self.rman_scene.scene_any_lights = self.rman_scene._scene_has_lights() - if not self.rman_scene.scene_any_lights: - self.rman_scene.default_light.SetHidden(0) - ''' - def update_cropwindow(self, cropwindow=None): if not self.rman_render.rman_interactive_running: return From 801a137b8f7e53d004bd20c7b57ce9a14b381bb7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 10:15:00 -0800 Subject: [PATCH 020/278] Allow quadrics to do transform blur. --- rman_translators/rman_quadric_translator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rman_translators/rman_quadric_translator.py b/rman_translators/rman_quadric_translator.py index 80605474..332314f3 100644 --- a/rman_translators/rman_quadric_translator.py +++ b/rman_translators/rman_quadric_translator.py @@ -1,5 +1,6 @@ from .rman_translator import RmanTranslator from ..rman_sg_nodes.rman_sg_quadric import RmanSgQuadric +from ..rfb_utils import object_utils class RmanQuadricTranslator(RmanTranslator): @@ -12,6 +13,9 @@ def export(self, ob, db_name): sg_node = self.rman_scene.sg_scene.CreateQuadric(db_name) rman_sg_quadric = RmanSgQuadric(self.rman_scene, sg_node, db_name) + if self.rman_scene.do_motion_blur: + rman_sg_quadric.is_transforming = object_utils.is_transforming(ob) + return rman_sg_quadric def export_deform_sample(self, rman_sg_quadric, ob, time_sample): From 24b889b04be53061038f0c8b0704526908241b64 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 10:55:16 -0800 Subject: [PATCH 021/278] Remove old export code path. Also, remove old dictionaries that are no longer needed. --- rman_scene.py | 537 ++++---------------------------------------------- 1 file changed, 37 insertions(+), 500 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 7474d06b..b7a16388 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -71,13 +71,11 @@ class RmanScene(object): is_viewport_render (bool) - whether we are rendering into Blender's viewport scene_solo_light (bool) - user has solo'd a light (all other lights are muted) rman_materials (dict) - dictionary of scene's materials - rman_objects (dict) - dictionary of all objects rman_translators (dict) - dictionary of all RmanTranslator(s) rman_particles (dict) - dictionary of all particle systems used rman_cameras (dict) - dictionary of all cameras in the scene obj_hash (dict) - dictionary of hashes to objects ( for object picking ) moving_objects (dict) - dictionary of objects that are moving/deforming in the scene - processed_obs (dict) - dictionary of objects already processed motion_steps (set) - the full set of motion steps for the scene, including overrides from individual objects main_camera (RmanSgCamera) - pointer to the main scene camera @@ -116,14 +114,12 @@ def __init__(self, rman_render=None): self.is_xpu = False self.rman_materials = dict() - self.rman_objects = dict() self.rman_translators = dict() self.rman_particles = dict() self.rman_cameras = dict() self.obj_hash = dict() self.moving_objects = dict() self.rman_prototypes = dict() - self.processed_obs = [] self.motion_steps = set() self.main_camera = None @@ -178,15 +174,12 @@ def _find_renderman_layer(self): def reset(self): # clear out dictionaries etc. self.rman_materials.clear() - self.rman_objects.clear() self.rman_particles.clear() self.rman_cameras.clear() self.obj_hash.clear() self.motion_steps = set() self.moving_objects.clear() self.rman_prototypes.clear() - - self.processed_obs.clear() self.render_default_light = False self.world_df_node = None @@ -270,7 +263,6 @@ def export_for_rib_selection(self, context, sg_scene): objs = context.selected_objects self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) self.export_data_blocks(objs) - self.export_instances(obj_selected=objs) def export_for_swatch_render(self, depsgraph, sg_scene): self.sg_scene = sg_scene @@ -315,8 +307,6 @@ def export(self): self.scene_any_lights = self._scene_has_lights() rfb_log().debug("Calling export_data_blocks()") - #self.export_data_blocks(bpy.data.objects) - #self.export_data_blocks([x for x in self.depsgraph.ids if isinstance(x, bpy.types.Object)]) self.export_data_blocks() self.export_searchpaths() @@ -337,9 +327,6 @@ def export(self): if self.do_motion_blur: rfb_log().debug("Calling export_instances_motion()") self.export_instances_motion() - else: - rfb_log().debug("Calling export_instances()") - #self.export_instances() self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) self.num_object_instances = len(self.depsgraph.object_instances) @@ -379,7 +366,7 @@ def export_bake_render_scene(self): rman_root_sg_node.SetAttributes(attrs) rfb_log().debug("Calling export_data_blocks()") - self.export_data_blocks(bpy.data.objects) + self.export_data_blocks() self.export_searchpaths() self.export_global_options() @@ -398,9 +385,6 @@ def export_bake_render_scene(self): if self.do_motion_blur: rfb_log().debug("Calling export_instances_motion()") self.export_instances_motion() - else: - rfb_log().debug("Calling export_instances()") - self.export_instances() options = self.sg_scene.GetOptions() bake_resolution = int(rm.rman_bake_illlum_res) @@ -446,8 +430,7 @@ def export_bake_brickmap_selected(self): self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) objects_needed = [x for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] objects_needed.append(ob) - self.export_data_blocks(objects_needed) - self.export_instances() + self.export_data_blocks(objects_needed) self.export_samplefilters() self.export_displayfilters() @@ -510,8 +493,7 @@ def export_swatch_render_scene(self): self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) rfb_log().debug("Calling export_data_blocks()") - self.export_data_blocks([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Object)]) - self.export_instances() + self.export_data_blocks() def export_root_sg_node(self): @@ -571,15 +553,6 @@ def export_materials(self, materials): if rman_sg_material: self.rman_materials[mat.original] = rman_sg_material - def export_data_blocks_old(self, data_blocks): - total = len(data_blocks) - for i, obj in enumerate(data_blocks): - if obj.type not in ('ARMATURE', 'CAMERA'): - ob = obj.evaluated_get(self.depsgraph) - self.export_data_block_old(ob) - rfb_log().debug(" Exported %d/%d data blocks... (%s)" % (i, total, obj.name)) - self.rman_render.stats_mgr.set_export_stats("Exporting data blocks",i/total) - def check_visibility(self, instance): if not self.is_interactive: return True @@ -624,10 +597,11 @@ def export_data_blocks(self, selected_objects=list()): continue if rman_type == 'LIGHT': - self.check_solo_light(rman_sg_node, ob_eval) + self.check_solo_light(rman_sg_node, ob_eval) - if rman_type == 'EMPTY': + # If this is an empty, just export it as a coordinate system + # along with any instance attributes/materials necessary self._export_hidden_instance(ob_eval, rman_sg_node) continue elif rman_type == 'LIGHTFILTER': @@ -639,12 +613,18 @@ def export_data_blocks(self, selected_objects=list()): group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_group_translator.export(ob, group_db_name) rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - - rman_group_translator.update_transform(ob_inst, rman_sg_group) + # Object attrs + translator = self.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob, rman_sg_group) + translator.export_object_id(ob, rman_sg_group, ob_inst) + + # Add any particles necessary if rman_sg_node.rman_sg_particle_group_node: if (len(ob.particle_systems) > 0) and ob_inst.show_particles: rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + # Attach a material if psys: self.attach_particle_material(psys.settings, parent, ob, rman_sg_group) @@ -668,24 +648,29 @@ def export_data_blocks(self, selected_objects=list()): rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group - - # object attrs - translator = self.rman_translators.get(rman_type, None) - if translator: - translator.export_object_attributes(ob, rman_sg_group) - translator.export_object_id(ob, rman_sg_group, ob_inst) + rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_type == "META": + # meta/blobbies are already in world space. Their instances don't need to + # set a transform. + return + + rman_group_translator.update_transform(ob_inst, rman_sg_group) def export_data_block(self, proto_key, ob): - rman_type = object_utils._detect_primitive_(ob) + rman_type = object_utils._detect_primitive_(ob) + + if rman_type == "META": + # only add the meta instance that matches the family name + if ob.name_full != object_utils.get_meta_family(ob): + return None if proto_key in self.rman_prototypes: return self.rman_prototypes[proto_key] translator = self.rman_translators.get(rman_type, None) if not translator: - return + return None rman_sg_node = None db_name = object_utils.get_db_name(ob) @@ -729,16 +714,15 @@ def export_data_block(self, proto_key, ob): translator.update(ob, rman_sg_node) - if rman_type in ['MESH', 'POINTS']: - # Deal with any particles now. Particles are children to mesh nodes. + if len(ob.particle_systems) > 0: + # Deal with any particles now. subframes = [] if self.do_motion_blur: subframes = scene_utils._get_subframes_(2, self.bl_scene) self.motion_steps.update(subframes) - if len(ob.particle_systems) > 0: - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) psys_translator = self.rman_translators['PARTICLES'] for psys in ob.particle_systems: @@ -754,7 +738,8 @@ def export_data_block(self, proto_key, ob): ob_psys[psys.settings.original] = rman_sg_particles self.rman_particles[proto_key] = ob_psys rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - elif rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): + + if rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): # Make sure empties that are hidden still go out. Children # could still be visible self._export_hidden_instance(ob, rman_sg_node) @@ -762,7 +747,6 @@ def export_data_block(self, proto_key, ob): return rman_sg_node - def export_instances_motion(self, selected_objects=list()): origframe = self.bl_scene.frame_current @@ -860,7 +844,8 @@ def export_instances_motion(self, selected_objects=list()): group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_sg_node.instances.get(group_db_name, None) if rman_sg_group: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() + if first_sample: + rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) # deformation blur @@ -880,121 +865,6 @@ def export_instances_motion(self, selected_objects=list()): rfb_log().debug(" Finished exporting motion instances") self.rman_render.stats_mgr.set_export_stats("Finished exporting motion instances", 100) - def export_data_block_old(self, db_ob): - - # FIXME? - # We currently export a unique geometry/mesh per Object - # This means we're not actually sharing datablocks per Object, even if they are shared - # in Blender. We do this for a couple of reasons: - # - # 1. Each object can have different modifiers applied. This includes applying a subdiv and/or bevel modifiers. - # 2. Each object may want a different number of deformation motion samples - # - # This is incredibly wasteful when these don't apply. We could try and detect this case and - # create a shareable geometry. - - obj = bpy.data.objects.get(db_ob.name, None) - if not obj and self.is_swatch_render: - obj = db_ob - elif obj.type != db_ob.type: - obj = db_ob - - if obj and obj.type not in ('ARMATURE', 'CAMERA'): - ob = obj.evaluated_get(self.depsgraph) - rman_type = object_utils._detect_primitive_(ob) - db_name = object_utils.get_db_name(ob, rman_type=rman_type) - if rman_type == 'LIGHT': - if ob.data.renderman.renderman_light_role == 'RMAN_LIGHTFILTER': - # skip if this is a light filter - # these will be exported when we do regular lights - return - - translator = self.rman_translators.get(rman_type, None) - if not translator: - return - - rman_sg_node = None - if ob.original in self.rman_objects: - return - - rman_sg_node = translator.export(ob, db_name) - if not rman_sg_node: - return - rman_sg_node.rman_type = rman_type - self.rman_objects[ob.original] = rman_sg_node - - if self.is_interactive and not ob.show_instancer_for_viewport: - rman_sg_node.sg_node.SetHidden(1) - elif not ob.show_instancer_for_render: - rman_sg_node.sg_node.SetHidden(1) - - if rman_type in ['MESH', 'POINTS']: - # Deal with any particles now. Particles are children to mesh nodes. - subframes = [] - if self.do_motion_blur: - subframes = scene_utils._get_subframes_(2, self.bl_scene) - self.motion_steps.update(subframes) - - if len(ob.particle_systems) > 0: - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) - - psys_translator = self.rman_translators['PARTICLES'] - for psys in ob.particle_systems: - psys_db_name = '%s' % psys.name - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - if not rman_sg_particles: - continue - - psys_translator.set_motion_steps(rman_sg_particles, subframes) - psys_translator.update(ob, psys, rman_sg_particles) - - ob_psys = self.rman_particles.get(ob.original, dict()) - ob_psys[psys.settings.original] = rman_sg_particles - self.rman_particles[ob.original] = ob_psys - self.rman_objects[psys.settings.original] = rman_sg_particles - self.processed_obs.append(psys.settings.original) - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - - elif rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): - # Make sure empties that are hidden still go out. Children - # could still be visible - self._export_hidden_instance(ob, rman_sg_node) - return rman_sg_node - - - # motion blur - # we set motion steps for this object, even if it's not moving - # it could be moving as part of a particle system - mb_segs = -1 - mb_deform_segs = -1 - if self.do_motion_blur: - mb_segs = self.bl_scene.renderman.motion_segments - mb_deform_segs = self.bl_scene.renderman.deform_motion_segments - if ob.renderman.motion_segments_override: - mb_segs = ob.renderman.motion_segments - if mb_segs > 1: - subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) - rman_sg_node.motion_steps = subframes - self.motion_steps.update(subframes) - - if ob.renderman.motion_segments_override: - mb_deform_segs = ob.renderman.deform_motion_segments - - if mb_deform_segs > 1: - subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) - rman_sg_node.deform_motion_steps = subframes - self.motion_steps.update(subframes) - - if rman_sg_node.is_transforming or rman_sg_node.is_deforming: - if mb_segs > 1 or mb_deform_segs > 1: - self.moving_objects[ob.name_full] = ob - - if mb_segs < 1: - rman_sg_node.is_transforming = False - if mb_deform_segs < 1: - rman_sg_node.is_deforming = False - def export_defaultlight(self): # Export a headlight light if needed if not self.default_light: @@ -1040,175 +910,6 @@ def _export_hidden_instance(self, ob, rman_sg_node): if ob.renderman.export_as_coordsys: self.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) - def _export_instance(self, ob_inst, seg=None): - - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_group_translator = self.rman_translators['GROUP'] - parent_sg_node = None - rman_sg_particles = None - psys = None - parent = None - if ob_inst.is_instance: - parent = ob_inst.parent - ob = ob_inst.instance_object - psys = ob_inst.particle_system - if psys: - # This object was instanced as part of a particle system. Add the object - # to particle system's owner' objects_instanced set. - parent_sg_node = self.rman_objects.get(parent.original, None) - if parent_sg_node: - parent_sg_node.objects_instanced.add(ob.original) - else: - #if parent.type == "EMPTY" and parent.is_instancer: - if parent.is_instancer: - parent_db_name = object_utils.get_db_name(parent) - parent_sg_node = self.rman_objects.get(parent.original, None) - if not parent_sg_node: - parent_sg_node = rman_group_translator.export(parent, parent_db_name) - self.rman_objects[parent.original] = parent_sg_node - - else: - ob = ob_inst.object - - if ob.type in ('ARMATURE', 'CAMERA'): - return - - rman_type = object_utils._detect_primitive_(ob) - if rman_type == 'LIGHTFILTER': - # light filters are part of lights, so when light instances - # are exported, light filterrs should go along with them - return - - elif ob.type == "EMPTY" and ob.is_instancer: - rman_sg_node = self.rman_objects.get(ob.original, None) - if not rman_sg_node: - empty_db_name = object_utils.get_db_name(ob) - rman_sg_node = rman_group_translator.export(ob, empty_db_name) - self.rman_objects[ob.original] = rman_sg_node - else: - if rman_type == 'EMPTY': - # this is just a regular empty object. - rman_sg_node = self.rman_objects.get(ob.original, None) - if rman_sg_node: - self._export_hidden_instance(ob, rman_sg_node) - return - - if rman_type == "META": - # only add the meta instance that matches the family name - if ob.name_full != object_utils.get_meta_family(ob): - return - - rman_sg_node = self.rman_objects.get(ob.original, None) - if not rman_sg_node: - return - - translator = self.rman_translators.get(rman_type, None) - if not translator: - return - - if group_db_name in rman_sg_node.instances: - # we've already added this instance - return - else: - - if not ob.original in self.processed_obs: - translator.update(ob, rman_sg_node) - translator.export_object_primvars(ob, rman_sg_node) - self.processed_obs.append(ob.original) - - rman_sg_group = rman_group_translator.export(ob, group_db_name) - if ob.is_instancer and ob.instance_type != 'NONE': - rman_sg_group.is_instancer = ob.is_instancer - if rman_sg_node.sg_node is None: - # add the group to the root anyways - db_name = object_utils.get_db_name(ob, rman_type=rman_type) - rman_sg_group.db_name = db_name - self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - self.rman_objects[ob.original] = rman_sg_group - return - - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - rman_sg_group.rman_sg_node_instance = rman_sg_node - - if rman_sg_node.rman_sg_particle_group_node: - if (len(ob.particle_systems) > 0) and ob_inst.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - if ob.parent and object_utils._detect_primitive_(ob.parent) == 'EMPTY': - # this object is a child of an empty. Add it to the empty. - rman_empty_node = self.rman_objects.get(ob.parent.original) - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) - else: - self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - - # add this instance to rman_sg_node - rman_sg_node.instances[group_db_name] = rman_sg_group - - # object attrs - translator.export_object_attributes(ob, rman_sg_group) - translator.export_object_id(ob, rman_sg_group, ob_inst) - - # attach material - if psys: - self.attach_particle_material(psys.settings, parent, ob, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original - else: - self.attach_material(ob, rman_sg_group) - - # check local view - if self.is_interactive: - if parent: - if not parent.visible_in_viewport_get(self.context.space_data): - rman_sg_group.sg_node.SetHidden(1) - else: - rman_sg_group.sg_node.SetHidden(-1) - else: - if not ob.visible_in_viewport_get(self.context.space_data): - rman_sg_group.sg_node.SetHidden(1) - else: - rman_sg_group.sg_node.SetHidden(-1) - - if rman_type == "META": - # meta/blobbies are already in world space. Their instances don't need to - # set a transform. - return - - if rman_sg_node.is_transforming: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) - rman_group_translator.update_transform_sample(ob_inst, rman_sg_group, 0, seg ) - elif psys and self.do_motion_blur: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) - rman_group_translator.update_transform_sample(ob_inst, rman_sg_group, 0, seg ) - else: - rman_group_translator.update_transform(ob_inst, rman_sg_group) - - def export_instances(self, obj_selected=None): - total = len(self.depsgraph.object_instances) - obj_selected_names = [] - if obj_selected: - obj_selected_names = [o.name for o in obj_selected] - for i, ob_inst in enumerate(self.depsgraph.object_instances): - if obj_selected: - objFound = False - - if ob_inst.is_instance: - if ob_inst.instance_object.name in obj_selected_names: - objFound = True - elif ob_inst.object.name in obj_selected_names: - objFound = True - - if not objFound: - continue - - #if not self.is_interactive and not ob_inst.show_self: - # continue - - self._export_instance(ob_inst) - self.rman_render.stats_mgr.set_export_stats("Exporting instances", i/total) - - rfb_log().debug(" Exported %d/%d instances..." % (i, total)) - def attach_material(self, ob, rman_sg_node): mat = object_utils.get_active_material(ob) if mat: @@ -1240,129 +941,6 @@ def attach_particle_material(self, psys_settings, parent, ob, group): scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) group.is_meshlight = rman_sg_material.has_meshlight - def export_instances_motion_old(self, obj_selected=None): - origframe = self.bl_scene.frame_current - - mb_segs = self.bl_scene.renderman.motion_segments - origframe = self.bl_scene.frame_current - - motion_steps = sorted(list(self.motion_steps)) - - first_sample = False - delta = -motion_steps[0] - for samp, seg in enumerate(motion_steps): - first_sample = (samp == 0) - if seg < 0.0: - self.rman_render.bl_engine.frame_set(origframe - 1, subframe=1.0 + seg) - else: - self.rman_render.bl_engine.frame_set(origframe, subframe=seg) - - self.depsgraph.update() - time_samp = seg + delta # get the normlized version of the segment - total = len(self.depsgraph.object_instances) - objFound = False - - # update camera - if not first_sample and self.main_camera.is_transforming and seg in self.main_camera.motion_steps: - cam_translator = self.rman_translators['CAMERA'] - idx = 0 - for i, s in enumerate(self.main_camera.motion_steps): - if s == seg: - idx = i - break - cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) - - for i, ob_inst in enumerate(self.depsgraph.object_instances): - if obj_selected: - if objFound: - break - - if ob_inst.is_instance: - if ob_inst.instance_object.name == obj_selected: - objFound = True - elif ob_inst.object.name == obj_selected.name: - objFound = True - - if not objFound: - continue - - if not ob_inst.show_self: - continue - - if first_sample: - # for the first motion sample use _export_instance() - self._export_instance(ob_inst, seg=time_samp) - self.rman_render.stats_mgr.set_export_stats("Exporting instances (%f)" % seg, i/total) - continue - - rman_group_translator = self.rman_translators['GROUP'] - psys = None - if ob_inst.is_instance: - ob = ob_inst.instance_object.original - psys = ob_inst.particle_system - else: - ob = ob_inst.object - - if ob.name_full not in self.moving_objects and not psys: - continue - - if ob.type not in ['MESH']: - continue - - group_db_name = object_utils.get_group_db_name(ob_inst) - - rman_sg_node = self.rman_objects.get(ob.original, None) - if not rman_sg_node: - continue - - if not seg in rman_sg_node.motion_steps: - continue - - idx = 0 - for i, s in enumerate(rman_sg_node.motion_steps): - if s == seg: - idx = i - break - - if rman_sg_node.is_transforming or psys: - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if rman_sg_group: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) # should have been set in _export_instances() - rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) - - self.rman_render.stats_mgr.set_export_stats("Exporting instances (%f)" % seg, i/total) - - for ob_original,rman_sg_node in self.rman_objects.items(): - ob = ob_original.evaluated_get(self.depsgraph) - psys_translator = self.rman_translators['PARTICLES'] - particle_systems = getattr(ob, 'particle_systems', list()) - for psys in particle_systems: - ob_psys = self.rman_particles.get(ob.original, dict()) - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if rman_sg_particles: - if not seg in rman_sg_particles.motion_steps: - continue - idx = 0 - for i, s in enumerate(rman_sg_node.motion_steps): - if s == seg: - idx = i - break - psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) - - if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: - rman_type = rman_sg_node.rman_type - if rman_type in ['MESH', 'FLUID']: - translator = self.rman_translators.get(rman_type, None) - if translator: - idx = 0 - for i, s in enumerate(rman_sg_node.deform_motion_steps): - if s == seg: - idx = i - break - translator.export_deform_sample(rman_sg_node, ob, idx) - - self.rman_render.bl_engine.frame_set(origframe, subframe=0) - def check_light_local_view(self, ob, rman_sg_node): if self.is_interactive and self.context.space_data: if not ob.visible_in_viewport_get(self.context.space_data): @@ -1384,41 +962,6 @@ def check_solo_light(self, rman_sg_node, ob): else: rman_sg_node.sg_node.SetHidden(1) - - ''' - if self.bl_scene.renderman.solo_light: - for light_ob in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False): - rman_sg_node = self.rman_objects.get(light_ob.original, None) - if not rman_sg_node: - continue - rm = light_ob.renderman - if not rm: - continue - if rm.solo: - rman_sg_node.sg_node.SetHidden(0) - else: - rman_sg_node.sg_node.SetHidden(1) - else: - for light_ob in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False): - rman_sg_node = self.rman_objects.get(light_ob.original, None) - if not rman_sg_node: - continue - rm = light_ob.renderman - if not rm: - continue - - if self.check_light_local_view(light_ob, rman_sg_node): - return - - if self.is_interactive: - if not light_ob.hide_get(): - rman_sg_node.sg_node.SetHidden(rm.mute) - else: - rman_sg_node.sg_node.SetHidden(1) - else: - rman_sg_node.sg_node.SetHidden(rm.mute) - ''' - def export_searchpaths(self): # TODO # RMAN_ARCHIVEPATH, @@ -1629,10 +1172,7 @@ def export_cameras(self, bl_cameras): # add camera so we don't mistake it for a new obj if main_cam: - self.rman_cameras[main_cam.original] = self.main_camera - self.rman_objects[main_cam.original] = self.main_camera - - self.processed_obs.append(main_cam.original) + self.rman_cameras[main_cam.original] = self.main_camera else: if self.is_interactive: main_cam = self.context.space_data.camera @@ -1641,7 +1181,6 @@ def export_cameras(self, bl_cameras): self.main_camera = rman_sg_camera if main_cam: self.rman_cameras[main_cam.original] = rman_sg_camera - self.rman_objects[main_cam.original] = rman_sg_camera # resolution cam_translator._update_render_resolution(main_cam, self.main_camera) @@ -1663,8 +1202,6 @@ def export_cameras(self, bl_cameras): self.rman_cameras[cam.original] = rman_sg_camera - self.rman_objects[cam.original] = rman_sg_camera - self.sg_scene.Root().AddChild(rman_sg_camera.sg_node) self.sg_scene.Root().AddCoordinateSystem(rman_sg_camera.sg_node) From e8ddd2c7b7b9c1d44a4f92c38e73cade9e0bbf7e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:05:15 -0800 Subject: [PATCH 022/278] Pass the selected objects as "selected_objects" to export_data_blocks when viewing the selected RIB. --- rman_scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index b7a16388..9b208ecd 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -262,7 +262,7 @@ def export_for_rib_selection(self, context, sg_scene): self.export_root_sg_node() objs = context.selected_objects self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - self.export_data_blocks(objs) + self.export_data_blocks(selected_objects=objs) def export_for_swatch_render(self, depsgraph, sg_scene): self.sg_scene = sg_scene @@ -430,7 +430,7 @@ def export_bake_brickmap_selected(self): self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) objects_needed = [x for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] objects_needed.append(ob) - self.export_data_blocks(objects_needed) + self.export_data_blocks(selected_objects=objects_needed) self.export_samplefilters() self.export_displayfilters() From 4fa60fa473ec191cd734846b3b28cad7ebb3c3ef Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:21:56 -0800 Subject: [PATCH 023/278] Allow for new Blender lights to be added during IPR --- rman_scene_sync.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 45a28d6d..d935bd6c 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -413,8 +413,6 @@ def update_scene(self, context, depsgraph): self.rman_scene.depsgraph = depsgraph self.rman_scene.bl_scene = depsgraph.scene self.rman_scene.context = context - - did_mesh_update = False # did the mesh actually update # Check the number of instances. If we differ, an object may have been # added or deleted @@ -459,7 +457,6 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.Mesh): rfb_log().debug("Mesh updated: %s" % obj.id.name) - did_mesh_update = True ''' # Experimental code path. We can use context.blend_data.user_map to ask # what objects use this mesh. We can then loop thru and call object_update on these @@ -606,12 +603,10 @@ def update_scene(self, context, depsgraph): pass #self.update_geometry_node_instances(obj.id) - - deleted_obj_keys = list() # list of potential objects to delete - already_udpated = list() # list of objects already updated during our loop - clear_instances = list() # list of objects who had their instances cleared if self.num_instances_changed or self.rman_updates: - deleted_obj_keys = list(self.rman_scene.rman_prototypes) + deleted_obj_keys = list(self.rman_scene.rman_prototypes) # list of potential objects to delete + already_udpated = list() # list of objects already updated during our loop + clear_instances = list() # list of objects who had their instances cleared rfb_log().debug("Updating instances") with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_group_translator = self.rman_scene.rman_translators['GROUP'] @@ -651,10 +646,6 @@ def update_scene(self, context, depsgraph): if not rman_sg_node: # this is a new object. - if rman_type == 'LIGHT': - # We don't support adding new Blender lights - if not shadergraph_utils.is_rman_light(ob_eval): - continue rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval) if not rman_sg_node: continue From c72d53cf2062c6117216343600007f145969a1bc Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:31:37 -0800 Subject: [PATCH 024/278] Add an RmanUpdate instance if we can't find the rman_sg_node for a light filter. --- rman_scene_sync.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index d935bd6c..c288bf07 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -184,13 +184,19 @@ def light_filter_updated(self, obj): proto_key = object_utils.prototype_key(ob) rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) if not rman_sg_node: + # Light filter needs to be added + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[ob.original] = rman_update return if obj.is_updated_transform or obj.is_updated_shading: rfb_log().debug("\tLight Filter: %s Transform Updated" % obj.id.name) self.light_filter_transform_updated(obj) if obj.is_updated_geometry: rfb_log().debug("\tLight Filter: %s Shading Updated" % obj.id.name) - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) for light_ob in rman_sg_node.lights_list: if isinstance(light_ob, bpy.types.Material): @@ -228,18 +234,6 @@ def camera_updated(self, ob_update): rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) translator._update_render_cam_transform(ob, rman_sg_camera) - def update_light_visibility(self, rman_sg_node, ob): - if not self.rman_scene.scene_solo_light: - rman_sg_node.sg_node.SetHidden(ob.renderman.mute) - else: - rm = ob.renderman - if not rm: - return - if rm.solo: - rman_sg_node.sg_node.SetHidden(0) - else: - rman_sg_node.sg_node.SetHidden(1) - def check_particle_systems(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) for psys in ob.particle_systems: From ac6633fa607fafb1b4884ec254a0570fd5400d1a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:37:13 -0800 Subject: [PATCH 025/278] Meta/blobbies don't need a transform. --- rman_scene_sync.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index c288bf07..5bd80183 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -743,8 +743,6 @@ def update_scene(self, context, depsgraph): else: self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - rman_group_translator.update_transform(instance, rman_sg_group) - if rman_type == 'LIGHT': # We are dealing with a light. Check if it's a solo light, or muted self.rman_scene.check_solo_light(rman_sg_node, ob_eval) @@ -753,8 +751,6 @@ def update_scene(self, context, depsgraph): if self.rman_scene.default_light.GetHidden() != 1: self.rman_scene.default_light.SetHidden(1) - continue - # Attach a material if psys: self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) @@ -783,6 +779,12 @@ def update_scene(self, context, depsgraph): rman_particles_node = ob_psys[k] self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) del ob_psys[k] + + if rman_type == 'META': + continue + + # Transform + rman_group_translator.update_transform(instance, rman_sg_group) # delete objects if deleted_obj_keys: From 84c3d592fe2c0596ab18cb41731ffc918fd4b097 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:44:21 -0800 Subject: [PATCH 026/278] Set rman_is_exporting is False, when trying to stop the render, otherwise the stats thread will get stuck. --- rman_render.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rman_render.py b/rman_render.py index 6f7a9675..ceaa9c87 100644 --- a/rman_render.py +++ b/rman_render.py @@ -1075,6 +1075,7 @@ def stop_render(self, stop_draw_thread=True): return self.rman_running = False + self.rman_is_exporting = False self.rman_interactive_running = False self.rman_swatch_render_running = False self.rman_is_viewport_rendering = False From bdf048f91029f8035b36303632b6920826fdc9af Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:49:48 -0800 Subject: [PATCH 027/278] Remove duplicate set of rman_is_exporting --- rman_render.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rman_render.py b/rman_render.py index bad50c8b..640f06f2 100644 --- a/rman_render.py +++ b/rman_render.py @@ -1075,7 +1075,6 @@ def stop_render(self, stop_draw_thread=True): return self.rman_running = False - self.rman_is_exporting = False self.rman_interactive_running = False self.rman_swatch_render_running = False self.rman_is_viewport_rendering = False From 262b1732a32b217191a1f574763d36ed7f01c093 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 11:58:13 -0800 Subject: [PATCH 028/278] Double check data actually exists when coming up with a prototype_key. Also, add some more logging to rman_render. --- rfb_utils/object_utils.py | 5 ++++- rman_render.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index aa2e5669..801efdc2 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -154,7 +154,10 @@ def has_empty_parent(ob): def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): if ob.is_instance: - return ob.object.data.name + if ob.object.data: + return ob.object.data.name + else: + return ob.object.name if ob.object.data: return ob.object.data.name return ob.object.original.name diff --git a/rman_render.py b/rman_render.py index 640f06f2..49b7a5b1 100644 --- a/rman_render.py +++ b/rman_render.py @@ -525,6 +525,7 @@ def start_render(self, depsgraph, for_background=False): self.rman_is_live_rendering = True except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False @@ -681,6 +682,7 @@ def start_external_render(self, depsgraph): self.sgmngr.DeleteScene(self.sg_scene) except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False @@ -773,6 +775,7 @@ def start_bake_render(self, depsgraph, for_background=False): self.start_stats_thread() except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False @@ -852,6 +855,7 @@ def start_external_bake_render(self, depsgraph): rfb_log().info("Finished parsing scene. Total time: %s" % string_utils._format_time_(time.time() - time_start)) except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False @@ -945,6 +949,7 @@ def start_interactive_render(self, context, depsgraph): return True except Exception as e: bpy.ops.renderman.printer('INVOKE_DEFAULT', level="ERROR", message='Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False @@ -1051,6 +1056,7 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.sg_scene.Render("rib " + rib_output + " -archive") except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().debug("Export failed: %s" % str(e)) self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False From aa36579b3c3ec5578964b024a1d834e8061c3e34 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 12:13:20 -0800 Subject: [PATCH 029/278] Make sure to export instance attributes when we re-add instances --- rman_scene.py | 4 ++-- rman_scene_sync.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 9b208ecd..336dda86 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -617,8 +617,8 @@ def export_data_blocks(self, selected_objects=list()): # Object attrs translator = self.rman_translators.get(rman_type, None) if translator: - translator.export_object_attributes(ob, rman_sg_group) - translator.export_object_id(ob, rman_sg_group, ob_inst) + translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_id(ob_eval, rman_sg_group, ob_inst) # Add any particles necessary if rman_sg_node.rman_sg_particle_group_node: diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 5bd80183..f3fc8e20 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -784,12 +784,18 @@ def update_scene(self, context, depsgraph): continue # Transform - rman_group_translator.update_transform(instance, rman_sg_group) + rman_group_translator.update_transform(instance, rman_sg_group) + + # Object attrs + translator = self.rman_scene.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_id(ob_eval, rman_sg_group, instance) # delete objects if deleted_obj_keys: - self.delete_objects(deleted_obj_keys) - + self.delete_objects(deleted_obj_keys) + # call txmake all in case of new textures texture_utils.get_txmanager().txmake_all(blocking=False) rfb_log().debug("------End update scene----------") From e7cd45b97668a3e6f4be8df0020dc59bd6ca0f18 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 13:14:56 -0800 Subject: [PATCH 030/278] Typo calling export_data_block --- rman_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_scene.py b/rman_scene.py index 336dda86..2a934a0b 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -896,7 +896,7 @@ def _export_hidden_instance(self, ob, rman_sg_node): rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) if not rman_empty_node: # Empty was not created. Export it. - rman_empty_node = self.export_data_block(parent_proto_key, ob.parent, None) + rman_empty_node = self.export_data_block(parent_proto_key, ob.parent) if not rman_empty_node: self.get_root_sg_node().AddChild(rman_sg_node.sg_node) translator.export_transform(ob, rman_sg_node.sg_node) From 0630228e1a5365f8be89a1d4d09ef623a5dd13f1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Sun, 6 Feb 2022 13:35:17 -0800 Subject: [PATCH 031/278] Add plp's time_this decorator, so we can time our functions. Move the depsgraph.object_instances loop in rman_scene_sync into its own function so we can time it. Functions only get timed if RFB_DEVELOPER is set. --- rfb_utils/timer_utils.py | 19 ++ rman_scene_sync.py | 383 ++++++++++++++++++++------------------- 2 files changed, 214 insertions(+), 188 deletions(-) create mode 100644 rfb_utils/timer_utils.py diff --git a/rfb_utils/timer_utils.py b/rfb_utils/timer_utils.py new file mode 100644 index 00000000..c3bacf1b --- /dev/null +++ b/rfb_utils/timer_utils.py @@ -0,0 +1,19 @@ +from .envconfig_utils import envconfig +import time + +def time_this(f): + if not envconfig().getenv('RFB_DEVELOPER'): + return f + """Function that can be used as a decorator to time any method.""" + def timed(*args, **kw): + tstart = time.time() + result = f(*args, **kw) + tstop = time.time() + elapsed = (tstop - tstart) * 1000.0 + print(' _ %0.2f ms: %s(%s, %s)' % ( + elapsed, f.__name__, + ', '.join([repr(a) for a in args]), + ', '.join(['%s=%r' % (k, w) for k, w in kw.items()]))) + return result + + return timed \ No newline at end of file diff --git a/rman_scene_sync.py b/rman_scene_sync.py index f3fc8e20..87580035 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -4,6 +4,7 @@ from .rfb_utils import texture_utils from .rfb_utils import scene_utils from .rfb_utils import shadergraph_utils +from .rfb_utils.timer_utils import time_this from .rfb_logger import rfb_log from .rman_sg_nodes.rman_sg_lightfilter import RmanSgLightFilter @@ -65,6 +66,7 @@ def update_view(self, context, depsgraph): else: translator.update_transform(camera, rman_sg_camera) + @time_this def scene_updated(self): # Check visible objects visible_objects = self.rman_scene.context.visible_objects @@ -598,208 +600,213 @@ def update_scene(self, context, depsgraph): #self.update_geometry_node_instances(obj.id) if self.num_instances_changed or self.rman_updates: - deleted_obj_keys = list(self.rman_scene.rman_prototypes) # list of potential objects to delete - already_udpated = list() # list of objects already updated during our loop - clear_instances = list() # list of objects who had their instances cleared - rfb_log().debug("Updating instances") - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - rman_group_translator = self.rman_scene.rman_translators['GROUP'] - for instance in depsgraph.object_instances: - if instance.object.type in ('ARMATURE', 'CAMERA'): - continue + self.check_instances() + + # call txmake all in case of new textures + texture_utils.get_txmanager().txmake_all(blocking=False) + rfb_log().debug("------End update scene----------") - ob_key = instance.object.original - ob_eval = instance.object.evaluated_get(depsgraph) - parent = None - psys = None - is_new_object = False - proto_key = object_utils.prototype_key(instance) - if instance.is_instance: - ob_key = instance.instance_object.original - psys = instance.particle_system - parent = instance.parent - - if proto_key in deleted_obj_keys: - deleted_obj_keys.remove(proto_key) + @time_this + def check_instances(self): + deleted_obj_keys = list(self.rman_scene.rman_prototypes) # list of potential objects to delete + already_udpated = list() # list of objects already updated during our loop + clear_instances = list() # list of objects who had their instances cleared + rfb_log().debug("Updating instances") + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + rman_group_translator = self.rman_scene.rman_translators['GROUP'] + for instance in self.rman_scene.depsgraph.object_instances: + if instance.object.type in ('ARMATURE', 'CAMERA'): + continue - rman_type = object_utils._detect_primitive_(ob_eval) + ob_key = instance.object.original + ob_eval = instance.object.evaluated_get(self.rman_scene.depsgraph) + parent = None + psys = None + is_new_object = False + proto_key = object_utils.prototype_key(instance) + if instance.is_instance: + ob_key = instance.instance_object.original + psys = instance.particle_system + parent = instance.parent - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) - if rman_sg_node and rman_type != rman_sg_node.rman_type: - # Types don't match - # - # This can happen because - # we have not been able to tag our types before Blender - # tells us an object has been added - # For now, just delete the existing sg_node - rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key) - self.clear_instances(ob_eval, rman_sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) - del self.rman_scene.rman_prototypes[proto_key] - rman_sg_node = None + if proto_key in deleted_obj_keys: + deleted_obj_keys.remove(proto_key) + rman_type = object_utils._detect_primitive_(ob_eval) + + rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + if rman_sg_node and rman_type != rman_sg_node.rman_type: + # Types don't match + # + # This can happen because + # we have not been able to tag our types before Blender + # tells us an object has been added + # For now, just delete the existing sg_node + rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key) + self.clear_instances(ob_eval, rman_sg_node) + self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) + del self.rman_scene.rman_prototypes[proto_key] + rman_sg_node = None + + if not rman_sg_node: + # this is a new object. + rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval) if not rman_sg_node: - # this is a new object. - rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval) - if not rman_sg_node: - continue - - rfb_log().debug("\tNew Object added: %s (%s)" % (proto_key, rman_type)) - - if rman_type == 'LIGHTFILTER': - # update all lights with this light filter - users = bpy.context.blend_data.user_map(subset={ob_eval.original}) - for o in users[ob_eval.original]: - if isinstance(o, bpy.types.Light): - o.node_tree.update_tag() - continue - - is_new_object = True - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update - rman_update.is_updated_geometry = False - - if self.num_instances_changed: - # If the number of instances has changed, - # we check all instances in the scene - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update - else: - if ob_key not in self.rman_updates: - if not parent: - continue - # check if the parent is also marked to be updated - if parent.original not in self.rman_updates: - continue - - # parent was marked needing update. - # create an on the fly RmanUpdate() - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update - else: - rman_update = self.rman_updates[ob_key] - - if rman_sg_node and not is_new_object: - if rman_update.is_updated_geometry and proto_key not in already_udpated: - translator = self.rman_scene.rman_translators.get(rman_type, None) - rfb_log().debug("\tUpdating Object: %s" % proto_key) - translator.update(ob_eval, rman_sg_node) - already_udpated.append(proto_key) - - if rman_type == 'EMPTY': - self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) - continue - - elif rman_type == 'LIGHTFILTER': - # Light filters are special. We don't need to add instances - # of them, as they are part of lights continue - if rman_sg_node not in clear_instances: - # clear all instances for this prototype, if - # we have not already done so - rfb_log().debug("\tClearing instances: %s" % proto_key) - self.clear_instances(ob_eval, rman_sg_node) - clear_instances.append(rman_sg_node) + rfb_log().debug("\tNew Object added: %s (%s)" % (proto_key, rman_type)) - if not self.rman_scene.check_visibility(instance): - # This instance is not visible in the viewport. Don't - # add an instance of it + if rman_type == 'LIGHTFILTER': + # update all lights with this light filter + users = bpy.context.blend_data.user_map(subset={ob_eval.original}) + for o in users[ob_eval.original]: + if isinstance(o, bpy.types.Light): + o.node_tree.update_tag() continue - group_db_name = object_utils.get_group_db_name(instance) - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if not rman_sg_group: - rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group - - if object_utils.has_empty_parent(ob_eval): - # this object is a child of an empty. Add it to the empty. - parent_proto_key = object_utils.prototype_key(ob_eval.parent) - rman_empty_node = self.rman_scene.rman_prototypes.get(parent_proto_key, None) - if not rman_empty_node: - ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) - rman_empty_node = self.rman_scene.export_data_block(parent_proto_key, ob_parent_eval) - self.rman_scene._export_hidden_instance(ob_parent_eval, rman_empty_node) - if parent_proto_key in deleted_obj_keys: - deleted_obj_keys.remove(parent_proto_key) - - if not rman_empty_node: - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - else: - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) - else: - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - - if rman_type == 'LIGHT': - # We are dealing with a light. Check if it's a solo light, or muted - self.rman_scene.check_solo_light(rman_sg_node, ob_eval) + is_new_object = True + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + rman_update.is_updated_geometry = False + + if self.num_instances_changed: + # If the number of instances has changed, + # we check all instances in the scene + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + else: + if ob_key not in self.rman_updates: + if not parent: + continue + # check if the parent is also marked to be updated + if parent.original not in self.rman_updates: + continue - # Hide the default light - if self.rman_scene.default_light.GetHidden() != 1: - self.rman_scene.default_light.SetHidden(1) - - # Attach a material - if psys: - self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original + # parent was marked needing update. + # create an on the fly RmanUpdate() + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update else: - self.rman_scene.attach_material(ob_eval, rman_sg_group) - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + rman_update = self.rman_updates[ob_key] + + if rman_sg_node and not is_new_object: + if rman_update.is_updated_geometry and proto_key not in already_udpated: + translator = self.rman_scene.rman_translators.get(rman_type, None) + rfb_log().debug("\tUpdating Object: %s" % proto_key) + translator.update(ob_eval, rman_sg_node) + already_udpated.append(proto_key) + + if rman_type == 'EMPTY': + self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) + continue + + elif rman_type == 'LIGHTFILTER': + # Light filters are special. We don't need to add instances + # of them, as they are part of lights + continue + + if rman_sg_node not in clear_instances: + # clear all instances for this prototype, if + # we have not already done so + rfb_log().debug("\tClearing instances: %s" % proto_key) + self.clear_instances(ob_eval, rman_sg_node) + clear_instances.append(rman_sg_node) + + if not self.rman_scene.check_visibility(instance): + # This instance is not visible in the viewport. Don't + # add an instance of it + continue + + group_db_name = object_utils.get_group_db_name(instance) + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if not rman_sg_group: + rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) rman_sg_node.instances[group_db_name] = rman_sg_group - if rman_sg_node.rman_sg_particle_group_node: - rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - if (len(ob_eval.particle_systems) > 0) and instance.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - # Delete any removed partcle systems - if proto_key in self.rman_scene.rman_particles: - ob_psys = self.rman_scene.rman_particles[proto_key] - rman_particle_nodes = list(ob_psys) - for psys in ob_eval.particle_systems: - try: - rman_particle_nodes.remove(psys.settings.original) - except: - continue - if rman_particle_nodes: - rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) - for k in rman_particle_nodes: - rman_particles_node = ob_psys[k] - self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) - del ob_psys[k] - - if rman_type == 'META': - continue - - # Transform - rman_group_translator.update_transform(instance, rman_sg_group) - - # Object attrs - translator = self.rman_scene.rman_translators.get(rman_type, None) - if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) - translator.export_object_id(ob_eval, rman_sg_group, instance) - - # delete objects - if deleted_obj_keys: - self.delete_objects(deleted_obj_keys) - - # call txmake all in case of new textures - texture_utils.get_txmanager().txmake_all(blocking=False) - rfb_log().debug("------End update scene----------") + if object_utils.has_empty_parent(ob_eval): + # this object is a child of an empty. Add it to the empty. + parent_proto_key = object_utils.prototype_key(ob_eval.parent) + rman_empty_node = self.rman_scene.rman_prototypes.get(parent_proto_key, None) + if not rman_empty_node: + ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) + rman_empty_node = self.rman_scene.export_data_block(parent_proto_key, ob_parent_eval) + self.rman_scene._export_hidden_instance(ob_parent_eval, rman_empty_node) + if parent_proto_key in deleted_obj_keys: + deleted_obj_keys.remove(parent_proto_key) + + if not rman_empty_node: + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + else: + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + else: + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + if rman_type == 'LIGHT': + # We are dealing with a light. Check if it's a solo light, or muted + self.rman_scene.check_solo_light(rman_sg_node, ob_eval) + + # Hide the default light + if self.rman_scene.default_light.GetHidden() != 1: + self.rman_scene.default_light.SetHidden(1) + + # Attach a material + if psys: + self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.rman_scene.attach_material(ob_eval, rman_sg_group) + self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_sg_node.rman_sg_particle_group_node: + rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + if (len(ob_eval.particle_systems) > 0) and instance.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + + # Delete any removed partcle systems + if proto_key in self.rman_scene.rman_particles: + ob_psys = self.rman_scene.rman_particles[proto_key] + rman_particle_nodes = list(ob_psys) + for psys in ob_eval.particle_systems: + try: + rman_particle_nodes.remove(psys.settings.original) + except: + continue + if rman_particle_nodes: + rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) + for k in rman_particle_nodes: + rman_particles_node = ob_psys[k] + self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) + del ob_psys[k] + + if rman_type == 'META': + continue + + # Transform + rman_group_translator.update_transform(instance, rman_sg_group) + + # Object attrs + translator = self.rman_scene.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_id(ob_eval, rman_sg_group, instance) + + # delete objects + if deleted_obj_keys: + self.delete_objects(deleted_obj_keys) + + @time_this def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("Deleting objects") for key in deleted_obj_keys: From c8b185bd62dffaa798f80abcd10c65d96f15a169 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 7 Feb 2022 08:11:30 -0800 Subject: [PATCH 032/278] Make sure portal lights get tagged to be updated when dome lights change. --- rman_scene_sync.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 87580035..14da9736 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -391,12 +391,8 @@ def update_geo_instances(nodes): update_geo_instances(modifier.node_group.nodes) def update_portals(self, ob): - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator = self.rman_scene.rman_translators['LIGHT'] - for portal in scene_utils.get_all_portals(ob): - rman_sg_node = self.rman_scene.rman_objects.get(portal.original, None) - if rman_sg_node: - translator.update(portal, rman_sg_node) + for portal in scene_utils.get_all_portals(ob): + portal.original.update_tag() def update_scene(self, context, depsgraph): @@ -756,6 +752,9 @@ def check_instances(self): if rman_type == 'LIGHT': # We are dealing with a light. Check if it's a solo light, or muted self.rman_scene.check_solo_light(rman_sg_node, ob_eval) + + # check portal lights + self.update_portals(ob_eval) # Hide the default light if self.rman_scene.default_light.GetHidden() != 1: From 99b1f892cb38d2d7f818a4ab083522f5db03e488 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 8 Feb 2022 08:04:51 -0800 Subject: [PATCH 033/278] Add a get_rman_protoype method to RmanScene. Use this in RmanSceneSync rather than accessing the rman_prototypes dictionary directly. --- rman_scene.py | 59 ++++++++++++++++++++-------------------------- rman_scene_sync.py | 33 ++++++++++---------------- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 2a934a0b..0cc71f9c 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -592,19 +592,14 @@ def export_data_blocks(self, selected_objects=list()): continue rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.export_data_block(proto_key, ob_eval) + rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) #self.export_data_block(proto_key, ob_eval) if not rman_sg_node: continue if rman_type == 'LIGHT': self.check_solo_light(rman_sg_node, ob_eval) - if rman_type == 'EMPTY': - # If this is an empty, just export it as a coordinate system - # along with any instance attributes/materials necessary - self._export_hidden_instance(ob_eval, rman_sg_node) - continue - elif rman_type == 'LIGHTFILTER': + if rman_type == 'LIGHTFILTER': # we don't need to create instances of light filters # we just need them to be added as coordinate systems # which should have been done in export @@ -634,18 +629,11 @@ def export_data_blocks(self, selected_objects=list()): if object_utils.has_empty_parent(ob): # this object is a child of an empty. Add it to the empty. + ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) parent_proto_key = object_utils.prototype_key(ob.parent) - rman_empty_node = self.rman_prototypes.get(parent_proto_key, None) - if not rman_empty_node: - ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) - rman_empty_node = self.export_data_block(parent_proto_key, ob_parent_eval) - self._export_hidden_instance(ob_parent_eval, rman_empty_node) - - if not rman_empty_node: - self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - else: - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_group.sg_node) rman_sg_node.instances[group_db_name] = rman_sg_group @@ -739,9 +727,9 @@ def export_data_block(self, proto_key, ob): self.rman_particles[proto_key] = ob_psys rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - if rman_type == 'EMPTY' and (ob.hide_render or ob.hide_viewport): - # Make sure empties that are hidden still go out. Children - # could still be visible + if rman_type == 'EMPTY': + # If this is an empty, just export it as a coordinate system + # along with any instance attributes/materials necessary self._export_hidden_instance(ob, rman_sg_node) return rman_sg_node @@ -885,7 +873,20 @@ def export_defaultlight(self): def _scene_has_lights(self): # Determine if there are any lights in the scene num_lights = len(scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)) - return num_lights > 0 + return num_lights > 0 + + def get_rman_prototype(self, proto_key, ob=None, create=False): + if proto_key in self.rman_prototypes: + return self.rman_prototypes[proto_key] + + if not create: + return None + + if not ob: + return None + + rman_sg_node = self.export_data_block(proto_key, ob) + return rman_sg_node def _export_hidden_instance(self, ob, rman_sg_node): translator = self.rman_translators.get('EMPTY') @@ -893,17 +894,9 @@ def _export_hidden_instance(self, ob, rman_sg_node): self.attach_material(ob, rman_sg_node) if object_utils.has_empty_parent(ob): parent_proto_key = object_utils.prototype_key(ob.parent) - rman_empty_node = self.rman_prototypes.get(ob.parent.original, None) - if not rman_empty_node: - # Empty was not created. Export it. - rman_empty_node = self.export_data_block(parent_proto_key, ob.parent) - if not rman_empty_node: - self.get_root_sg_node().AddChild(rman_sg_node.sg_node) - translator.export_transform(ob, rman_sg_node.sg_node) - if ob.renderman.export_as_coordsys: - self.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) - else: - rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) + ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) + rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_node.sg_node) translator.export_transform(ob, rman_sg_node.sg_node) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 14da9736..5cacc205 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -133,7 +133,7 @@ def _mesh_light_update(self, mat): if ob.type in ('ARMATURE', 'CURVE', 'CAMERA'): continue proto_key = object_utils.prototype_key(ob_inst) - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) if rman_sg_node: found = False for name, material in ob.data.materials.items(): @@ -175,7 +175,7 @@ def material_updated(self, obj): def light_filter_transform_updated(self, obj): ob = obj.id.evaluated_get(self.rman_scene.depsgraph) proto_key = object_utils.prototype_key(ob) - rman_sg_lightfilter = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) if rman_sg_lightfilter: rman_group_translator = self.rman_scene.rman_translators['GROUP'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -184,7 +184,7 @@ def light_filter_transform_updated(self, obj): def light_filter_updated(self, obj): ob = obj.id.evaluated_get(self.rman_scene.depsgraph) proto_key = object_utils.prototype_key(ob) - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) if not rman_sg_node: # Light filter needs to be added rman_update = RmanUpdate() @@ -283,7 +283,7 @@ def update_empty(self, ob_update, rman_sg_node=None): else: rfb_log().debug("\tRegular empty") proto_key = object_utils.prototype_key(ob) - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) if not rman_sg_node: return translator = self.rman_scene.rman_translators['EMPTY'] @@ -300,7 +300,7 @@ def clear_instances(self, ob, rman_sg_node=None): for k,rman_sg_group in rman_sg_node.instances.items(): if object_utils.has_empty_parent(ob): proto_key = object_utils.prototype_key(ob.parent) - rman_empty_node = self.rman_scene.rman_prototypes.get(proto_key) + rman_empty_node = self.rman_scene.get_rman_prototype(proto_key) if rman_empty_node: rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) else: @@ -518,7 +518,7 @@ def update_scene(self, context, depsgraph): rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) ob_psys[psys.settings.original] = rman_sg_particles self.rman_scene.rman_particles[proto_key] = ob_psys - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) if rman_sg_node: if not rman_sg_node.rman_sg_particle_group_node: particles_group_db = '' @@ -630,7 +630,7 @@ def check_instances(self): rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.rman_scene.rman_prototypes.get(proto_key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) if rman_sg_node and rman_type != rman_sg_node.rman_type: # Types don't match # @@ -732,20 +732,11 @@ def check_instances(self): if object_utils.has_empty_parent(ob_eval): # this object is a child of an empty. Add it to the empty. + ob_parent_eval = ob_eval.parent.evaluated_get(self.rman_scene.depsgraph) parent_proto_key = object_utils.prototype_key(ob_eval.parent) - rman_empty_node = self.rman_scene.rman_prototypes.get(parent_proto_key, None) - if not rman_empty_node: - ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) - rman_empty_node = self.rman_scene.export_data_block(parent_proto_key, ob_parent_eval) - self.rman_scene._export_hidden_instance(ob_parent_eval, rman_empty_node) - if parent_proto_key in deleted_obj_keys: - deleted_obj_keys.remove(parent_proto_key) - - if not rman_empty_node: - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - else: - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + rman_empty_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) else: self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) @@ -809,7 +800,7 @@ def check_instances(self): def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("Deleting objects") for key in deleted_obj_keys: - rman_sg_node = self.rman_scene.rman_prototypes.get(key, None) + rman_sg_node = self.rman_scene.get_rman_prototype(key) if not rman_sg_node: continue From 9cb74d3a93488f2207ea247fe3690ca7a8a4a7a2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 8 Feb 2022 14:28:24 -0800 Subject: [PATCH 034/278] Add a get_rman_particles method, similar to get_rman_prototype. Merge some duplicate code for particles. --- rman_scene.py | 18 ++++++++ rman_scene_sync.py | 104 +++++++++++++++++---------------------------- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 0cc71f9c..c186a65e 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -888,6 +888,24 @@ def get_rman_prototype(self, proto_key, ob=None, create=False): rman_sg_node = self.export_data_block(proto_key, ob) return rman_sg_node + def get_rman_particles(self, proto_key, psys, ob): + psys_translator = self.rman_translators['PARTICLES'] + group_translator = self.rman_translators['GROUP'] + ob_psys = self.rman_particles.get(proto_key, dict()) + rman_sg_particles = ob_psys.get(psys.settings.original, None) + if not rman_sg_particles: + psys_db_name = '%s' % psys.name + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + ob_psys[psys.settings.original] = rman_sg_particles + self.rman_particles[proto_key] = ob_psys + rman_sg_node = self.get_rman_prototype(proto_key) + if rman_sg_node: + if not rman_sg_node.rman_sg_particle_group_node: + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = group_translator.export(None, particles_group_db) + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + return rman_sg_particles + def _export_hidden_instance(self, ob, rman_sg_node): translator = self.rman_translators.get('EMPTY') translator.export_object_attributes(ob, rman_sg_node) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 5cacc205..52c2fcdb 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -1,9 +1,7 @@ # utils from .rfb_utils import object_utils -from .rfb_utils import transform_utils from .rfb_utils import texture_utils from .rfb_utils import scene_utils -from .rfb_utils import shadergraph_utils from .rfb_utils.timer_utils import time_this from .rfb_logger import rfb_log @@ -236,33 +234,47 @@ def camera_updated(self, ob_update): rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) translator._update_render_cam_transform(ob, rman_sg_camera) + + def check_particle_instancer(self, ob_update, psys): + # this particle system is a instancer + inst_ob = getattr(psys.settings, 'instance_object', None) + collection = getattr(psys.settings, 'instance_collection', None) + if inst_ob: + if inst_ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[inst_ob.original] = rman_update + elif collection: + for col_obj in collection.all_objects: + if not col_obj.original.data: + continue + if col_obj.original in self.rman_updates: + continue + rman_update = RmanUpdate() + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + self.rman_updates[col_obj.original] = rman_update + def check_particle_systems(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) for psys in ob.particle_systems: if object_utils.is_particle_instancer(psys): - # this particle system is a instancer - inst_ob = getattr(psys.settings, 'instance_object', None) - collection = getattr(psys.settings, 'instance_collection', None) - if inst_ob: - if inst_ob.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry - rman_update.is_updated_shading = ob_update.is_updated_shading - rman_update.is_updated_transform = ob_update.is_updated_transform - self.rman_updates[inst_ob.original] = rman_update - elif collection: - for col_obj in collection.all_objects: - if not col_obj.original.data: - continue - if col_obj.original in self.rman_updates: - continue - rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry - rman_update.is_updated_shading = ob_update.is_updated_shading - rman_update.is_updated_transform = ob_update.is_updated_transform - self.rman_updates[col_obj.original] = rman_update - continue + self.check_particle_instancer(ob_update, psys) + + def update_particle_emitter(self, ob, psys): + psys_translator = self.rman_scene.rman_translators['PARTICLES'] + proto_key = object_utils.prototype_key(ob) + rman_sg_particles = self.rman_scene.get_rman_particles(proto_key, psys, ob) + psys_translator.update(ob, psys, rman_sg_particles) + def update_particle_emitters(self, ob): + for psys in ob.particle_systems: + if not object_utils.is_particle_instancer(psys): + self.update_particle_emitter(ob, psys) + def update_empty(self, ob_update, rman_sg_node=None): ob = ob_update.id rfb_log().debug("Update empty: %s" % ob.name) @@ -489,44 +501,10 @@ def update_scene(self, context, depsgraph): if not psys: continue if object_utils.is_particle_instancer(psys): - inst_ob = getattr(psys.settings, 'instance_object', None) - collection = getattr(psys.settings, 'instance_collection', None) - if inst_ob: - if inst_ob.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[inst_ob.original] = rman_update - elif collection: - for col_obj in collection.all_objects: - if not col_obj.original.data: - continue - if col_obj.original in self.rman_updates: - continue - rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[col_obj.original] = rman_update + self.check_particle_instancer(obj, psys) else: - psys_db_name = '%s' % psys.name - proto_key = object_utils.prototype_key(ob) - ob_psys = self.rman_scene.rman_particles.get(proto_key, dict()) - rman_sg_particles = ob_psys.get(obj.id.original, None) - if not rman_sg_particles: - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - ob_psys[psys.settings.original] = rman_sg_particles - self.rman_scene.rman_particles[proto_key] = ob_psys - rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) - if rman_sg_node: - if not rman_sg_node.rman_sg_particle_group_node: - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_scene.rman_translators['GROUP'].export(None, particles_group_db) - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - psys_translator.update(ob, psys, rman_sg_particles) + self.update_particle_emitter(ob, psys) - elif isinstance(obj.id, bpy.types.ShaderNodeTree): if obj.id.name in bpy.data.node_groups: # this is probably one of our fake node groups with ramps @@ -565,10 +543,7 @@ def update_scene(self, context, depsgraph): rman_update.is_updated_geometry = obj.is_updated_geometry rman_update.is_updated_shading = obj.is_updated_shading rman_update.is_updated_transform = obj.is_updated_transform - if obj.id.data: - proto_key = obj.id.original.data.original - else: - proto_key = obj.id.original + rfb_log().debug("\tObject: %s Updated" % obj.id.name) rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) @@ -700,6 +675,7 @@ def check_instances(self): translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) translator.update(ob_eval, rman_sg_node) + self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) if rman_type == 'EMPTY': From 944ec074b729ba208aab7b68f8de230d8eb6863d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 8 Feb 2022 15:54:09 -0800 Subject: [PATCH 035/278] Fixes for the focus object on the camera. We need to tag a camera be updated, if the object that just transformed is also focus object. --- rman_scene_sync.py | 40 +++++----------------- rman_translators/rman_camera_translator.py | 10 +++--- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 52c2fcdb..00a6cf15 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -341,9 +341,6 @@ def update_materials_dict(self, mat): def update_collection(self, coll): # mark all objects in a collection # as needing their instances updated - # the collection could have been updated with new objects - # FIXME: like grease pencil above we seem to crash when removing and adding instances - # of curves, we need to figure out what's going on for o in coll.all_objects: if o.type in ('ARMATURE', 'CAMERA'): continue @@ -353,26 +350,6 @@ def update_collection(self, coll): rman_update.is_updated_transform = True self.rman_updates[o.original] = rman_update - ''' - rman_type = object_utils._detect_primitive_(o) - rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) - if not rman_sg_node: - if not self.update_objects_dict(o, rman_type=rman_type): - self.new_objects.add(o) - self.update_instances.add(o) - continue - - if rman_type == 'LIGHT': - # Check light visibility. Light visibility is already handled elsewhere - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - if self.rman_scene.check_light_local_view(o, rman_sg_node): - continue - - self.update_instances.add(o.original) - self.update_particles.add(o) - self.update_geometry_node_instances(o) - ''' - def update_geometry_node_instances(self, obj): def update_geo_instances(nodes): # look for all point instance nodes @@ -517,9 +494,9 @@ def update_scene(self, context, depsgraph): elif hasattr(o, 'node_tree'): o.node_tree.update_tag() - elif isinstance(obj.id, bpy.types.Object): - ob_data = bpy.data.objects.get(ob.name, ob) - rman_type = object_utils._detect_primitive_(ob_data) + elif isinstance(obj.id, bpy.types.Object): + ob_eval = obj.id.evaluated_get(depsgraph) + rman_type = object_utils._detect_primitive_(ob_eval) if ob.type in ('ARMATURE'): continue @@ -554,12 +531,11 @@ def update_scene(self, context, depsgraph): # Check if this object is the focus object the camera. If it is # we need to update the camera - rman_sg_camera = self.rman_scene.main_camera - if rman_sg_camera.rman_focus_object and rman_sg_camera.rman_focus_object == rman_sg_node: - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - cam_object = translator.find_scene_camera() - translator.update(cam_object, rman_sg_camera) + if obj.is_updated_transform: + for camera in bpy.data.cameras: + rm = camera.renderman + if rm.rman_focus_object and rm.rman_focus_object.original == ob_eval.original: + camera.update_tag() elif isinstance(obj.id, bpy.types.Collection): diff --git a/rman_translators/rman_camera_translator.py b/rman_translators/rman_camera_translator.py index b8bfdba3..b2f1fbbd 100644 --- a/rman_translators/rman_camera_translator.py +++ b/rman_translators/rman_camera_translator.py @@ -453,8 +453,9 @@ def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): rman_sg_camera.use_focus_object = cam_rm.rman_focus_object if cam_rm.rman_focus_object: dof_focal_distance = (ob.location - cam_rm.rman_focus_object.location).length - rman_sg_node = self.rman_scene.rman_objects.get(cam_rm.rman_focus_object.original, None) - rman_sg_camera.rman_focus_object = rman_sg_node + rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(cam_rm.rman_focus_object)) + if rman_sg_node: + rman_sg_camera.rman_focus_object = rman_sg_node else: dof_focal_distance = cam_rm.rman_focus_distance rman_sg_camera.rman_focus_object = None @@ -526,8 +527,9 @@ def _set_fov(self, ob, rman_sg_camera, cam, aspectratio, projparams): rman_sg_camera.use_focus_object = cam_rm.rman_focus_object if cam_rm.rman_focus_object: dof_focal_distance = (ob.location - cam_rm.rman_focus_object.location).length - rman_sg_node = self.rman_scene.rman_objects.get(cam_rm.rman_focus_object.original, None) - rman_sg_camera.rman_focus_object = rman_sg_node + rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(cam_rm.rman_focus_object)) + if rman_sg_node: + rman_sg_camera.rman_focus_object = rman_sg_node else: dof_focal_distance = cam_rm.rman_focus_distance rman_sg_camera.rman_focus_object = None From 016146cd958ea46431b2b2b0415abd5c43616c91 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 9 Feb 2022 07:49:23 -0800 Subject: [PATCH 036/278] Add a destructor to rman_sg_node. The destructor should be the one responsible for calling DeleteDagNode --- rman_scene.py | 1 + rman_scene_sync.py | 30 +++--------------------------- rman_sg_nodes/rman_sg_fluid.py | 8 +++++++- rman_sg_nodes/rman_sg_node.py | 13 +++++++++++++ 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index c186a65e..9a18d4bb 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -181,6 +181,7 @@ def reset(self): self.moving_objects.clear() self.rman_prototypes.clear() + self.main_camera = None self.render_default_light = False self.world_df_node = None self.default_light = None diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 00a6cf15..59a31849 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -241,8 +241,7 @@ def check_particle_instancer(self, ob_update, psys): collection = getattr(psys.settings, 'instance_collection', None) if inst_ob: if inst_ob.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update = RmanUpdate() rman_update.is_updated_shading = ob_update.is_updated_shading rman_update.is_updated_transform = ob_update.is_updated_transform self.rman_updates[inst_ob.original] = rman_update @@ -253,7 +252,6 @@ def check_particle_instancer(self, ob_update, psys): if col_obj.original in self.rman_updates: continue rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry rman_update.is_updated_shading = ob_update.is_updated_shading rman_update.is_updated_transform = ob_update.is_updated_transform self.rman_updates[col_obj.original] = rman_update @@ -307,19 +305,7 @@ def update_empty(self, ob_update, rman_sg_node=None): self.rman_scene.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) else: self.rman_scene.get_root_sg_node().RemoveCoordinateSystem(rman_sg_node.sg_node) - - def clear_instances(self, ob, rman_sg_node=None): - for k,rman_sg_group in rman_sg_node.instances.items(): - if object_utils.has_empty_parent(ob): - proto_key = object_utils.prototype_key(ob.parent) - rman_empty_node = self.rman_scene.get_rman_prototype(proto_key) - if rman_empty_node: - rman_empty_node.sg_node.RemoveChild(rman_sg_group.sg_node) - else: - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_group.sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_group.sg_node) - rman_sg_node.instances.clear() - + def update_materials_dict(self, mat): # Try to see if we already have a material with the same db_name # We need to do this because undo/redo causes all bpy.types.ID @@ -590,8 +576,6 @@ def check_instances(self): # tells us an object has been added # For now, just delete the existing sg_node rfb_log().debug("\tTypes don't match. Removing: %s" % proto_key) - self.clear_instances(ob_eval, rman_sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) del self.rman_scene.rman_prototypes[proto_key] rman_sg_node = None @@ -667,7 +651,7 @@ def check_instances(self): # clear all instances for this prototype, if # we have not already done so rfb_log().debug("\tClearing instances: %s" % proto_key) - self.clear_instances(ob_eval, rman_sg_node) + rman_sg_node.clear_instances() clear_instances.append(rman_sg_node) if not self.rman_scene.check_visibility(instance): @@ -728,8 +712,6 @@ def check_instances(self): if rman_particle_nodes: rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) for k in rman_particle_nodes: - rman_particles_node = ob_psys[k] - self.rman_scene.sg_scene.DeleteDagNode(rman_particles_node.sg_node) del ob_psys[k] if rman_type == 'META': @@ -757,17 +739,11 @@ def delete_objects(self, deleted_obj_keys=list()): continue rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) - self.clear_instances(None, rman_sg_node) if key in self.rman_scene.rman_particles: rfb_log().debug("\t\tRemoving particles...") ob_psys = self.rman_scene.rman_particles[key] - for k, v in ob_psys.items(): - self.rman_scene.sg_scene.DeleteDagNode(v.sg_node) del self.rman_scene.rman_particles[key] - - self.rman_scene.get_root_sg_node().RemoveChild(rman_sg_node.sg_node) - self.rman_scene.sg_scene.DeleteDagNode(rman_sg_node.sg_node) del self.rman_scene.rman_prototypes[key] # We just deleted a light filter. We need to tell all lights diff --git a/rman_sg_nodes/rman_sg_fluid.py b/rman_sg_nodes/rman_sg_fluid.py index 07cbb561..e3ab786c 100644 --- a/rman_sg_nodes/rman_sg_fluid.py +++ b/rman_sg_nodes/rman_sg_fluid.py @@ -5,4 +5,10 @@ class RmanSgFluid(RmanSgNode): def __init__(self, rman_scene, sg_node, db_name): super().__init__(rman_scene, sg_node, db_name) self.rman_sg_volume_node = None - self.rman_sg_liquid_node = None \ No newline at end of file + self.rman_sg_liquid_node = None + + def __del__(self): + if self.rman_scene.rman_render.rman_running and self.rman_scene.sg_scene: + self.rman_scene.sg_scene.DeleteDagNode(self.rman_sg_volume_node) + self.rman_scene.sg_scene.DeleteDagNode(self.rman_sg_liquid_node) + super().__del__() \ No newline at end of file diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index f9c92ac3..cfb50a75 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -1,3 +1,6 @@ +from ..rfb_logger import rfb_log + + class RmanSgNode(object): ''' RmanSgNode and subclasses are meant to be a thin layer class around a RixSceneGraph node. @@ -53,6 +56,16 @@ def __init__(self, rman_scene, sg_node, db_name): # psys self.bl_psys_settings = None + def __del__(self): + if self.rman_scene.rman_render.rman_running and self.rman_scene.sg_scene: + #rfb_log().debug("Deleting dag node: %s" % self.db_name) + self.instances.clear() + self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) + + def clear_instances(self): + if self.rman_scene.rman_render.rman_running: + self.instances.clear() + @property def rman_scene(self): return self.__rman_scene From 725e3035e51b7065a154ae656a58f87fc360d45b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 9 Feb 2022 08:15:52 -0800 Subject: [PATCH 037/278] Fixes to get updating frame sensitive objects working again. --- rfb_utils/property_utils.py | 14 ++++++++------ rman_scene_sync.py | 31 ++++++++++++++----------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 4583e914..109537b0 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -441,7 +441,7 @@ def vstruct_conditional(node, param): new_tokens.extend(['else', 'False']) return eval(" ".join(new_tokens)) -def set_frame_sensitive(rman_sg_node, prop): +def check_frame_sensitive(prop): # check if the prop value has any frame token # ex: , , etc. # if it does, it means we need to issue a material @@ -449,9 +449,8 @@ def set_frame_sensitive(rman_sg_node, prop): pat = re.compile(r'<[f|F]\d*>') m = pat.search(prop) if m: - rman_sg_node.is_frame_sensitive = True - else: - rman_sg_node.is_frame_sensitive = False + return True + return False def set_dspymeta_params(node, prop_name, params): if node.plugin_name not in ['openexr', 'deepexr']: @@ -650,6 +649,7 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group set_pxrosl_params(node, rman_sg_node, params, ob=ob, mat_name=mat_name) return params + is_frame_sensitive = False for prop_name, meta in node.prop_meta.items(): bl_prop_info = BlPropInfo(node, prop_name, meta) param_widget = bl_prop_info.widget @@ -756,8 +756,8 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group val = [0, 0, 0] if param_type == 'color' else 0 elif param_type == 'string': - if rman_sg_node: - set_frame_sensitive(rman_sg_node, prop) + if not is_frame_sensitive: + is_frame_sensitive = check_frame_sensitive(prop) val = string_utils.expand_string(prop) options = meta['options'] @@ -773,6 +773,8 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group val = string_utils.convert_val(prop, type_hint=param_type) set_rix_param(params, param_type, param_name, val, is_reference=False, node=node) + + rman_sg_node.is_frame_sensitive = is_frame_sensitive return params diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 59a31849..dc31c49c 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -97,30 +97,28 @@ def scene_updated(self): # are marked as frame sensitive rfb_log().debug("Frame changed: %d -> %d" % (self.rman_scene.bl_frame_current, self.rman_scene.bl_scene.frame_current)) self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current - material_translator = self.rman_scene.rman_translators["MATERIAL"] self.frame_number_changed = True + # check for frame sensitive objects + for o in self.rman_scene.depsgraph.objects: + if o.type == 'CAMERA': + rman_sg_node = self.rman_cameras.get(o.original, None) + else: + rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(o), create=False) + if rman_sg_node and rman_sg_node.is_frame_sensitive: + o.update_tag() + + for mat in bpy.data.materials: + rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) + if rman_sg_material and rman_sg_material.is_frame_sensitive: + mat.node_tree.update_tag() + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): # update frame number options = self.rman_scene.sg_scene.GetOptions() options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.rman_scene.bl_frame_current) self.rman_scene.sg_scene.SetOptions(options) - for mat in bpy.data.materials: - db_name = object_utils.get_db_name(mat) - rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) - if rman_sg_material and rman_sg_material.is_frame_sensitive: - material_translator.update(mat, rman_sg_material) - - ''' - for o in bpy.data.objects: - if o.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[o.original] = rman_update - ''' - def _mesh_light_update(self, mat): object_list = list() with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): @@ -139,7 +137,6 @@ def _mesh_light_update(self, mat): found = True if found: - self.clear_instances(ob, rman_sg_node) del self.rman_scene.rman_prototypes[proto_key] if ob not in object_list: object_list.append(ob) From 85fc4940840fbd33f219090195b6924434bd036d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 10 Feb 2022 07:14:31 -0800 Subject: [PATCH 038/278] Fix typo. self.rman_cameras -> self.rman_scene.rman_cameras. --- rman_scene_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index dc31c49c..f54189ae 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -102,7 +102,7 @@ def scene_updated(self): # check for frame sensitive objects for o in self.rman_scene.depsgraph.objects: if o.type == 'CAMERA': - rman_sg_node = self.rman_cameras.get(o.original, None) + rman_sg_node = self.rman_scene.rman_cameras.get(o.original, None) else: rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(o), create=False) if rman_sg_node and rman_sg_node.is_frame_sensitive: From 31fbc52be568e9d62eae80000b1f2848917d57e4 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 10 Feb 2022 07:42:21 -0800 Subject: [PATCH 039/278] Add some debug logging when the number of instances changed and/or the number of visible objects changed. --- rman_scene_sync.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index f54189ae..97a8e608 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -70,6 +70,7 @@ def scene_updated(self): visible_objects = self.rman_scene.context.visible_objects if not self.num_instances_changed: if len(visible_objects) != self.rman_scene.num_objects_in_viewlayer: + rfb_log().debug("\tNumber of visible objects changed: %d -> %d" % (self.rman_scene.num_objects_in_viewlayer, len(visible_objects))) # The number of visible objects has changed. # Figure out the difference using sets set1 = set(self.rman_scene.objects_in_viewlayer) @@ -376,15 +377,17 @@ def update_scene(self, context, depsgraph): self.rman_scene.depsgraph = depsgraph self.rman_scene.bl_scene = depsgraph.scene - self.rman_scene.context = context + self.rman_scene.context = context + + rfb_log().debug("------Start update scene--------") # Check the number of instances. If we differ, an object may have been # added or deleted if self.rman_scene.num_object_instances != len(depsgraph.object_instances): + rfb_log().debug("\tNumber of instances changed: %d -> %d" % (self.rman_scene.num_object_instances, len(depsgraph.object_instances))) self.num_instances_changed = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) - rfb_log().debug("------Start update scene--------") for obj in reversed(depsgraph.updates): ob = obj.id From 1435ab907a9c49f317033f2a0934ceabb0f37440 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 10 Feb 2022 08:06:30 -0800 Subject: [PATCH 040/278] Re-instate the code path looks for the subdivision modifier --- rfb_utils/object_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 801efdc2..d8b629a6 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -99,7 +99,6 @@ def is_subdmesh(ob): return False rman_subdiv_scheme = getattr(ob.data.renderman, 'rman_subdiv_scheme', 'none') - return (rman_subdiv_scheme != 'none') if rm.primitive == 'AUTO' and rman_subdiv_scheme == 'none': return (is_subd_last(ob) or is_subd_displace_last(ob)) From d94a824813571fb20692233522919d8cc27ccb2e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 15 Feb 2022 11:16:58 -0800 Subject: [PATCH 041/278] Fix issues with external rendering. Make sure to set the sg_scene to None after calling DeleteScene. Fix typo in the RmanSgNode destructor. --- rfb_utils/property_utils.py | 3 ++- rman_render.py | 5 ++++- rman_sg_nodes/rman_sg_node.py | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 109537b0..c5758d4a 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -774,7 +774,8 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group set_rix_param(params, param_type, param_name, val, is_reference=False, node=node) - rman_sg_node.is_frame_sensitive = is_frame_sensitive + if rman_sg_node: + rman_sg_node.is_frame_sensitive = is_frame_sensitive return params diff --git a/rman_render.py b/rman_render.py index 49b7a5b1..3764874b 100644 --- a/rman_render.py +++ b/rman_render.py @@ -679,7 +679,10 @@ def start_external_render(self, depsgraph): frame=frame, asFilePath=True) self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) - self.sgmngr.DeleteScene(self.sg_scene) + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() + except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) rfb_log().debug("Export failed: %s" % str(e)) diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index cfb50a75..ad889201 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -57,10 +57,11 @@ def __init__(self, rman_scene, sg_node, db_name): self.bl_psys_settings = None def __del__(self): - if self.rman_scene.rman_render.rman_running and self.rman_scene.sg_scene: - #rfb_log().debug("Deleting dag node: %s" % self.db_name) + if self.rman_scene.rman_render.rman_running and self.rman_scene.rman_render.sg_scene: + rfb_log().debug("Deleting dag node: %s" % self.db_name) self.instances.clear() - self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) + if isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Group): + self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) def clear_instances(self): if self.rman_scene.rman_render.rman_running: From 65e4f7ce547f5b9d0e270a5d7e7e0ff3cfea95a0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 15 Feb 2022 12:00:45 -0800 Subject: [PATCH 042/278] Add a debug log message to mention which frame we're exporting when doing external RIB renders. --- rman_render.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rman_render.py b/rman_render.py index 3764874b..7fe91c62 100644 --- a/rman_render.py +++ b/rman_render.py @@ -672,6 +672,7 @@ def start_external_render(self, depsgraph): self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) try: self.bl_engine.frame_set(frame, subframe=0.0) + rfb_log().debug("Frame: %d" % frame) self.rman_is_exporting = True self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) self.rman_is_exporting = False From 3ceab93049f23a9e0889492259c71f2a6de85a67 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 15 Feb 2022 14:25:13 -0800 Subject: [PATCH 043/278] Update RmanSgNode destructor to use DeleteMaterial if the scene graph node is a material. --- rman_sg_nodes/rman_sg_node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index ad889201..b5e6635d 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -62,6 +62,8 @@ def __del__(self): self.instances.clear() if isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Group): self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) + elif isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Material): + self.rman_scene.sg_scene.DeleteMaterial(self.sg_node) def clear_instances(self): if self.rman_scene.rman_render.rman_running: From 66b59efeddfb712ee9100385b3136feff817320d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 16 Feb 2022 07:08:45 -0800 Subject: [PATCH 044/278] When checking the object visibility, just check the visibility of the object itself and not the parent, in the case of if it's an instanace. --- rman_scene.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 9a18d4bb..bcb1ffb2 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -558,13 +558,13 @@ def check_visibility(self, instance): if not self.is_interactive: return True viewport = self.context.space_data - ob_eval = instance.object.evaluated_get(self.depsgraph) - + ob_eval = instance.object.evaluated_get(self.depsgraph) + ''' if instance.is_instance: ob_eval = instance.instance_object.original.evaluated_get(self.depsgraph) if instance.parent: ob_eval = instance.parent - + ''' visible = ob_eval.visible_in_viewport_get(viewport) return visible @@ -590,10 +590,11 @@ def export_data_blocks(self, selected_objects=list()): parent = ob_inst.parent if not self.check_visibility(ob_inst): + rfb_log().debug(" Object (%s) not visible" % (ob.name)) continue rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) #self.export_data_block(proto_key, ob_eval) + rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) if not rman_sg_node: continue From 9388dc8f981a90e46862ef886abbabde81f50ab7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 08:07:29 -0800 Subject: [PATCH 045/278] Comment out a debug print --- rman_sg_nodes/rman_sg_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index b5e6635d..c2b005ed 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -58,7 +58,7 @@ def __init__(self, rman_scene, sg_node, db_name): def __del__(self): if self.rman_scene.rman_render.rman_running and self.rman_scene.rman_render.sg_scene: - rfb_log().debug("Deleting dag node: %s" % self.db_name) + #rfb_log().debug("Deleting dag node: %s" % self.db_name) self.instances.clear() if isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Group): self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) From 0f811b5142d1555fab3e2c25a3c674cd035b2c29 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 08:39:05 -0800 Subject: [PATCH 046/278] Need to use ob.original when checking for trace set membership. --- rman_scene_sync.py | 18 +++++++++++------- rman_translators/rman_translator.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 97a8e608..0dd2a284 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -470,6 +470,8 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.ShaderNodeTree): if obj.id.name in bpy.data.node_groups: + if len(obj.id.nodes) < 1: + continue # this is probably one of our fake node groups with ramps # update all of the users of this node tree rfb_log().debug("ShaderNodeTree updated: %s" % obj.id.name) @@ -694,7 +696,15 @@ def check_instances(self): else: self.rman_scene.attach_material(ob_eval, rman_sg_group) self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + # Object attrs + translator = self.rman_scene.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_id(ob_eval, rman_sg_group, instance) + rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_sg_node.rman_sg_particle_group_node: rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) if (len(ob_eval.particle_systems) > 0) and instance.show_particles: @@ -719,13 +729,7 @@ def check_instances(self): # Transform rman_group_translator.update_transform(instance, rman_sg_group) - - # Object attrs - translator = self.rman_scene.rman_translators.get(rman_type, None) - if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) - translator.export_object_id(ob_eval, rman_sg_group, instance) - + # delete objects if deleted_obj_keys: self.delete_objects(deleted_obj_keys) diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index a58a484d..1be0a613 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -236,7 +236,7 @@ def export_object_attributes(self, ob, rman_sg_node): lpe_groups_str = "*" for obj_group in self.rman_scene.bl_scene.renderman.object_groups: for member in obj_group.members: - if member.ob_pointer == ob: + if member.ob_pointer.original == ob.original: obj_groups_str += ',' + obj_group.name lpe_groups_str += ',' + obj_group.name break From 26ab15e063dd0eb203cdf2a3e77c5bf3a623f151 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 10:53:24 -0800 Subject: [PATCH 047/278] Modify our create objects operators, so they don't trigger multiple despgraph updates. --- rfb_utils/shadergraph_utils.py | 20 ++++ rman_operators/rman_operators_view3d.py | 133 ++++++++++++------------ 2 files changed, 84 insertions(+), 69 deletions(-) diff --git a/rfb_utils/shadergraph_utils.py b/rfb_utils/shadergraph_utils.py index e0462953..7f638ad9 100644 --- a/rfb_utils/shadergraph_utils.py +++ b/rfb_utils/shadergraph_utils.py @@ -837,6 +837,26 @@ def has_stylized_pattern_node(ob, node=None): return False +def create_bxdf(bxdf): + mat = bpy.data.materials.new(bxdf) + + mat.use_nodes = True + nt = mat.node_tree + + output = nt.nodes.new('RendermanOutputNode') + default = nt.nodes.new('%sBxdfNode' % bxdf) + default.location = output.location + default.location[0] -= 300 + nt.links.new(default.outputs[0], output.inputs[0]) + output.inputs[1].hide = True + output.inputs[3].hide = True + default.update_mat(mat) + + if bxdf == 'PxrLayerSurface': + create_pxrlayer_nodes(nt, default) + + return mat + def create_pxrlayer_nodes(nt, bxdf): from .. import rman_bl_nodes diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index 6e771031..b113830d 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -78,15 +78,13 @@ def description(cls, context, properties): def execute(self, context): - if self.properties.bl_prim_type != '': - bpy.ops.object.add(type=self.properties.bl_prim_type) - else: - bpy.ops.object.add(type='EMPTY') - ob = None - for o in context.selected_objects: - ob = o - break + nm = self.rman_default_name + data_block = None + if self.properties.bl_prim_type == 'VOLUME': + data_block = bpy.data.volumes.new(nm) + + ob = bpy.data.objects.new(nm, data_block) ob.empty_display_type = 'PLAIN_AXES' rm = ob.renderman @@ -98,16 +96,14 @@ def execute(self, context): ob.name = 'Ri%s' % rm.rman_quadric_type.capitalize() if rm.rman_quadric_type == 'SPHERE': ob.empty_display_type = 'SPHERE' - else: - if self.properties.rman_default_name != '': - ob.name = self.properties.rman_default_name + else: if rm.primitive == 'RI_VOLUME': ob.empty_display_type = 'CUBE' - bpy.ops.node.rman_new_material_override('EXEC_DEFAULT', bxdf_name='PxrVolume') + mat = shadergraph_utils.create_bxdf('PxrVolume') + ob.renderman.rman_material_override = mat elif self.properties.bl_prim_type == 'VOLUME': - bpy.ops.object.rman_add_bxdf('EXEC_DEFAULT', bxdf_name='PxrVolume') - mat = ob.active_material - nt = mat.node_tree + mat = shadergraph_utils.create_bxdf('PxrVolume') + ob.active_material = mat output = shadergraph_utils.find_node(mat, 'RendermanOutputNode') bxdf = output.inputs['Bxdf'].links[0].from_node bxdf.densityFloatPrimVar = 'density' @@ -130,7 +126,11 @@ def execute(self, context): yup_to_zup = mathutils.Matrix.Rotation(math.radians(90.0), 4, 'X') ob.matrix_world = yup_to_zup @ ob.matrix_world - ob.update_tag(refresh={'DATA'}) + context.scene.collection.objects.link(ob) + if context.view_layer.objects.active: + context.view_layer.objects.active.select_set(False) + ob.select_set(True) + context.view_layer.objects.active = ob return {"FINISHED"} @@ -183,23 +183,15 @@ def description(cls, context, properties): return info def execute(self, context): - bpy.ops.object.light_add(type='AREA') - light_ob = getattr(context, 'object', None) - if not light_ob: - if hasattr(context, 'selected_objects'): - light_ob = context.selected_objects[0] - else: - scene = context.scene - light_ob = scene.view_layers[0].objects.active - - light_ob.data.renderman.renderman_light_role = 'RMAN_LIGHT' - light_ob.data.renderman.renderman_lock_light_type = True - light_ob.data.use_nodes = True - light_ob.data.renderman.use_renderman_node = True - light_ob.name = self.rman_light_name - light_ob.data.name = self.rman_light_name - - nt = light_ob.data.node_tree + light = bpy.data.lights.new(self.rman_light_name, 'AREA') + light_ob = bpy.data.objects.new(self.rman_light_name, light) + + light.renderman.renderman_light_role = 'RMAN_LIGHT' + light.renderman.renderman_lock_light_type = True + light.use_nodes = True + light.renderman.use_renderman_node = True + + nt = light.node_tree output = nt.nodes.new('RendermanOutputNode') default = nt.nodes.new('%sLightNode' % self.rman_light_name) default.location = output.location @@ -208,7 +200,13 @@ def execute(self, context): output.inputs[0].hide = True output.inputs[2].hide = True output.inputs[3].hide = True - light_ob.data.renderman.renderman_light_shader = self.rman_light_name + light.renderman.renderman_light_shader = self.rman_light_name + + context.scene.collection.objects.link(light_ob) + if context.view_layer.objects.active: + context.view_layer.objects.active.select_set(False) + light_ob.select_set(True) + context.view_layer.objects.active = light_ob return {"FINISHED"} @@ -232,17 +230,15 @@ def description(cls, context, properties): def execute(self, context): selected_objects = context.selected_objects - bpy.ops.object.light_add(type='AREA') - light_filter_ob = context.object - light_filter_ob.data.renderman.renderman_light_role = 'RMAN_LIGHTFILTER' - light_filter_ob.data.renderman.renderman_lock_light_type = True - light_filter_ob.data.use_nodes = True - light_filter_ob.data.renderman.use_renderman_node = True - - light_filter_ob.name = self.rman_lightfilter_name - light_filter_ob.data.name = self.rman_lightfilter_name + light_filter = bpy.data.lights.new(self.rman_lightfilter_name, 'AREA') + light_filter_ob = bpy.data.objects.new(self.rman_lightfilter_name, light_filter) + + light_filter.renderman.renderman_light_role = 'RMAN_LIGHTFILTER' + light_filter.renderman.renderman_lock_light_type = True + light_filter.use_nodes = True + light_filter.renderman.use_renderman_node = True - nt = light_filter_ob.data.node_tree + nt = light_filter.node_tree output = nt.nodes.new('RendermanOutputNode') default = nt.nodes.new('%sLightfilterNode' % self.rman_lightfilter_name) default.location = output.location @@ -251,7 +247,13 @@ def execute(self, context): output.inputs[0].hide = True output.inputs[1].hide = True output.inputs[2].hide = True - light_filter_ob.data.renderman.renderman_light_filter_shader = self.rman_lightfilter_name + light_filter.renderman.renderman_light_filter_shader = self.rman_lightfilter_name + + context.scene.collection.objects.link(light_filter_ob) + if context.view_layer.objects.active: + context.view_layer.objects.active.select_set(False) + light_filter_ob.select_set(True) + context.view_layer.objects.active = light_filter_ob if self.properties.add_to_selected: for ob in selected_objects: @@ -263,7 +265,7 @@ def execute(self, context): mat = ob.active_material if mat: light_filter_item = mat.renderman_light.light_filters.add() - light_filter_item.linked_filter_ob = light_filter_ob + light_filter_item.linked_filter_ob = light_filter_ob return {"FINISHED"} @@ -287,34 +289,22 @@ def execute(self, context): selection = bpy.context.selected_objects if hasattr( bpy.context, 'selected_objects') else [] bxdf_name = self.properties.bxdf_name - mat = bpy.data.materials.new(bxdf_name) - - mat.use_nodes = True - nt = mat.node_tree - - output = nt.nodes.new('RendermanOutputNode') - default = nt.nodes.new('%sBxdfNode' % bxdf_name) - default.location = output.location - default.location[0] -= 300 - nt.links.new(default.outputs[0], output.inputs[0]) - output.inputs[1].hide = True - output.inputs[3].hide = True - default.update_mat(mat) - - if bxdf_name == 'PxrLayerSurface': - shadergraph_utils.create_pxrlayer_nodes(nt, default) - + mat = shadergraph_utils.create_bxdf(bxdf_name) for obj in selection: if(obj.type not in EXCLUDED_OBJECT_TYPES): if obj.type == 'EMPTY': obj.renderman.rman_material_override = mat else: material_slots = getattr(obj, 'material_slots', None) - if material_slots: + if material_slots is None: + continue + if len(material_slots) < 1: obj.active_material = mat else: - bpy.ops.object.material_slot_add() - obj.active_material = mat + material_slot = material_slots[0] + material_slot.material = mat + obj.active_material_index = 0 + obj.active_material = mat return {"FINISHED"} class PRMAN_OT_RM_Create_MeshLight(bpy.types.Operator): @@ -359,11 +349,16 @@ def execute(self, context): continue mat = self.create_mesh_light_material(context) material_slots = getattr(obj, 'material_slots', None) - if material_slots: + + if material_slots is None: + continue + if len(material_slots) < 1: obj.active_material = mat else: - bpy.ops.object.material_slot_add() - obj.active_material = mat + material_slot = material_slots[0] + material_slot.material = mat + obj.active_material_index = 0 + obj.active_material = mat return {"FINISHED"} From 972226a07213589bd28ffad5e581274c240de7d2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 12:56:09 -0800 Subject: [PATCH 048/278] Add a separate bool to mean check all instances (check_all_instances), rather than trying to overload the meaning of num_instances_changed --- rman_scene_sync.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 0dd2a284..fdd58978 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -37,6 +37,7 @@ def __init__(self, rman_render=None, rman_scene=None, sg_scene=None): self.sg_scene = sg_scene self.num_instances_changed = False # if the number of instances has changed since the last update self.frame_number_changed = False + self.check_all_instances = False # force checking all instances self.rman_updates = dict() # A dicitonary to hold RmanUpdate instances @@ -88,7 +89,7 @@ def scene_updated(self): self.rman_updates[o.original] = rman_update except: continue - self.num_instances_changed = True + self.check_all_instances = True self.rman_scene.num_objects_in_viewlayer = len(visible_objects) self.rman_scene.objects_in_viewlayer = [o for o in visible_objects] @@ -372,8 +373,9 @@ def update_scene(self, context, depsgraph): ## FIXME: this function is waaayyy too big and is doing too much stuff self.rman_updates = dict() - self.num_instances_changed = False # if the number of instances has changed since the last update + self.num_instances_changed = False self.frame_number_changed = False + self.check_all_instances = False self.rman_scene.depsgraph = depsgraph self.rman_scene.bl_scene = depsgraph.scene @@ -386,6 +388,7 @@ def update_scene(self, context, depsgraph): if self.rman_scene.num_object_instances != len(depsgraph.object_instances): rfb_log().debug("\tNumber of instances changed: %d -> %d" % (self.rman_scene.num_object_instances, len(depsgraph.object_instances))) self.num_instances_changed = True + self.check_all_instances = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) for obj in reversed(depsgraph.updates): @@ -534,7 +537,7 @@ def update_scene(self, context, depsgraph): pass #self.update_geometry_node_instances(obj.id) - if self.num_instances_changed or self.rman_updates: + if self.check_all_instances or self.rman_updates: self.check_instances() # call txmake all in case of new textures @@ -606,7 +609,7 @@ def check_instances(self): self.rman_updates[ob_key] = rman_update rman_update.is_updated_geometry = False - if self.num_instances_changed: + if self.check_all_instances: # If the number of instances has changed, # we check all instances in the scene rman_update = self.rman_updates.get(ob_key, None) From 9c40737735e17d5eb99b760ea6468e359a6bd3cf Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 13:02:19 -0800 Subject: [PATCH 049/278] Update the comment about check_all_instances --- rman_scene_sync.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index fdd58978..23348846 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -610,8 +610,7 @@ def check_instances(self): rman_update.is_updated_geometry = False if self.check_all_instances: - # If the number of instances has changed, - # we check all instances in the scene + # check all instances in the scene rman_update = self.rman_updates.get(ob_key, None) if not rman_update: rman_update = RmanUpdate() From 1e831f911d102d9cee55520a6751ae53b1bdb269 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 14:22:15 -0800 Subject: [PATCH 050/278] Fix issues with trying to update materials that are frame sensitive. --- rfb_utils/property_utils.py | 2 +- rman_scene_sync.py | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index c5758d4a..14915952 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -775,7 +775,7 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group set_rix_param(params, param_type, param_name, val, is_reference=False, node=node) if rman_sg_node: - rman_sg_node.is_frame_sensitive = is_frame_sensitive + rman_sg_node.is_frame_sensitive = (rman_sg_node.is_frame_sensitive or is_frame_sensitive) return params diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 23348846..23285c36 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -102,18 +102,21 @@ def scene_updated(self): self.frame_number_changed = True # check for frame sensitive objects - for o in self.rman_scene.depsgraph.objects: - if o.type == 'CAMERA': - rman_sg_node = self.rman_scene.rman_cameras.get(o.original, None) - else: - rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(o), create=False) - if rman_sg_node and rman_sg_node.is_frame_sensitive: - o.update_tag() - - for mat in bpy.data.materials: - rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) - if rman_sg_material and rman_sg_material.is_frame_sensitive: - mat.node_tree.update_tag() + for id in self.rman_scene.depsgraph.ids: + if isinstance(id, bpy.types.Object): + o = id.original + if o.type == 'CAMERA': + rman_sg_node = self.rman_scene.rman_cameras.get(o.original, None) + else: + rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(o), create=False) + if rman_sg_node and rman_sg_node.is_frame_sensitive: + if o.original not in self.rman_updates: + o.original.update_tag() + elif isinstance(id, bpy.types.Material): + mat = id.original + rman_sg_material = self.rman_scene.rman_materials.get(mat, None) + if rman_sg_material and rman_sg_material.is_frame_sensitive: + mat.node_tree.update_tag() with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): # update frame number From 4cd13d79655287fef8a74a0f6369c9d50a3a1072 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Feb 2022 16:04:01 -0800 Subject: [PATCH 051/278] Forgot to call export_object_primvars when we are updating our prototypes. --- rman_scene.py | 1 + rman_scene_sync.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rman_scene.py b/rman_scene.py index bcb1ffb2..508e8919 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -703,6 +703,7 @@ def export_data_block(self, proto_key, ob): rman_sg_node.is_deforming = False translator.update(ob, rman_sg_node) + translator.export_object_primvars(ob, rman_sg_node) if len(ob.particle_systems) > 0: # Deal with any particles now. diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 23285c36..89734754 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -641,7 +641,8 @@ def check_instances(self): if rman_update.is_updated_geometry and proto_key not in already_udpated: translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) - translator.update(ob_eval, rman_sg_node) + translator.update(ob_eval, rman_sg_node) + translator.export_object_primvars(ob_eval, rman_sg_node) self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) From 1b4c22404ac7f6f2497e7e225ce9e4700a5eb36a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Feb 2022 08:21:15 -0800 Subject: [PATCH 052/278] Missed a couple of places where we need to call RmanScene::reset() --- rman_render.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/rman_render.py b/rman_render.py index 7fe91c62..79966062 100644 --- a/rman_render.py +++ b/rman_render.py @@ -716,7 +716,9 @@ def start_external_render(self, depsgraph): self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) rfb_log().debug("Finished writing RIB. Time: %s" % string_utils._format_time_(time.time() - rib_time_start)) rfb_log().info("Finished parsing scene. Total time: %s" % string_utils._format_time_(time.time() - time_start)) - self.sgmngr.DeleteScene(self.sg_scene) + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) self.stop_render(stop_draw_thread=False) @@ -727,7 +729,6 @@ def start_external_render(self, depsgraph): spooler = rman_spool.RmanSpool(self, self.rman_scene, depsgraph) spooler.batch_render() self.rman_running = False - self.sg_scene = None self.del_bl_engine() return True @@ -829,7 +830,9 @@ def start_external_bake_render(self, depsgraph): self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False - self.sgmngr.DeleteScene(self.sg_scene) + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() self.bl_engine.frame_set(original_frame, subframe=0.0) @@ -865,12 +868,13 @@ def start_external_bake_render(self, depsgraph): return False self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() if rm.queuing_system != 'none': spooler = rman_spool.RmanSpool(self, self.rman_scene, depsgraph) spooler.batch_render() self.rman_running = False - self.sg_scene = None self.del_bl_engine() return True @@ -1043,7 +1047,9 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.stop_render(stop_draw_thread=False) self.del_bl_engine() return False - self.sgmngr.DeleteScene(self.sg_scene) + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() bl_scene.frame_set(original_frame, subframe=0.0) else: config = rman.Types.RtParamList() @@ -1065,9 +1071,9 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.del_bl_engine() return False self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() - - self.sg_scene = None self.rman_running = False return True From a0ec5f189744ba3686759a79b5bcf8862963355d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Feb 2022 09:41:38 -0800 Subject: [PATCH 053/278] Instead of passing a selected_objects list, use select_get() to figure out if something is selected. --- rman_scene.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 508e8919..f86b8b8c 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -261,9 +261,8 @@ def export_for_rib_selection(self, context, sg_scene): self.depsgraph = context.evaluated_depsgraph_get() self.export_root_sg_node() - objs = context.selected_objects self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - self.export_data_blocks(selected_objects=objs) + self.export_data_blocks(selected_objects=True) def export_for_swatch_render(self, depsgraph, sg_scene): self.sg_scene = sg_scene @@ -567,8 +566,25 @@ def check_visibility(self, instance): ''' visible = ob_eval.visible_in_viewport_get(viewport) return visible + + def is_instance_selected(self, instance): + ob = instance.object + parent = None + if instance.is_instance: + parent = instance.parent + + if not ob.original.select_get(): + if parent: + if not parent.original.select_get(): + return False + else: + return False + if parent and not parent.original.select_get(): + return False + + return True - def export_data_blocks(self, selected_objects=list()): + def export_data_blocks(self, selected_objects=False): rman_group_translator = self.rman_translators['GROUP'] total = len(self.depsgraph.object_instances) for i, ob_inst in enumerate(self.depsgraph.object_instances): @@ -577,9 +593,13 @@ def export_data_blocks(self, selected_objects=list()): self.rman_render.stats_mgr.set_export_stats("Exporting instances",i/total) if ob.type in ('ARMATURE', 'CAMERA'): continue - if selected_objects: - if ob.original not in selected_objects: - continue + + if selected_objects and not self.is_instance_selected(ob_inst): + continue + + if not self.check_visibility(ob_inst): + rfb_log().debug(" Object (%s) not visible" % (ob.name)) + continue ob_eval = ob.evaluated_get(self.depsgraph) psys = None @@ -587,11 +607,7 @@ def export_data_blocks(self, selected_objects=list()): proto_key = object_utils.prototype_key(ob_inst) if ob_inst.is_instance: psys = ob_inst.particle_system - parent = ob_inst.parent - - if not self.check_visibility(ob_inst): - rfb_log().debug(" Object (%s) not visible" % (ob.name)) - continue + parent = ob_inst.parent rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) From 40db840a3d6ebeed89c056badcd5fcc394182a72 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Feb 2022 14:55:43 -0800 Subject: [PATCH 054/278] Update the meta info for the addon. --- __init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d0d8415b..9338df54 100644 --- a/__init__.py +++ b/__init__.py @@ -38,8 +38,9 @@ "author": "Pixar", "version": (24, 4, 0), "blender": (2, 83, 0), - "location": "Info Header, render engine menu", + "location": "Render Properties > Render Engine > RenderMan", "description": "RenderMan 24 integration", + "doc_url": "https://rmanwiki.pixar.com/display/RFB", "warning": "", "category": "Render"} From b39960dc76dc15393cd41eee33a8a560db986f05 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Feb 2022 16:00:45 -0800 Subject: [PATCH 055/278] Add the -bbox flag to Render when we are exporting a RIB for the selected object. We can then try to use this bbox to resize the empty cube when loading the RIB file as an archive. --- rfb_utils/transform_utils.py | 23 ++++++++++++++++++++++- rman_operators/rman_operators_view3d.py | 12 ++++++++++++ rman_render.py | 9 +++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/rfb_utils/transform_utils.py b/rfb_utils/transform_utils.py index 8a81eb99..51d28648 100644 --- a/rfb_utils/transform_utils.py +++ b/rfb_utils/transform_utils.py @@ -1,5 +1,5 @@ import rman -from mathutils import Matrix +from mathutils import Matrix,Vector def convert_matrix(m): v = [m[0][0], m[1][0], m[2][0], m[3][0], @@ -21,6 +21,27 @@ def convert_matrix4x4(m): return rman_mtx +def get_world_bounding_box(selected_obs): + min_list = list() + max_list = list() + + min_vector = None + max_vector = None + + for ob in selected_obs: + v = ob.matrix_world @ Vector(ob.bound_box[0]) + if min_vector is None: + min_vector = v + elif v < min_vector: + min_vector = v + v = ob.matrix_world @ Vector(ob.bound_box[7]) + if max_vector is None: + max_vector = v + elif v > max_vector: + max_vector = v + + return "%f %f %f %f %f %f" % (min_vector[0], max_vector[0], min_vector[1], max_vector[1], min_vector[2], max_vector[2]) + def convert_ob_bounds(ob_bb): return (ob_bb[0][0], ob_bb[7][0], ob_bb[0][1], ob_bb[7][1], ob_bb[0][2], ob_bb[1][2]) diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index b113830d..6a61e696 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -110,7 +110,19 @@ def execute(self, context): if self.properties.rman_open_filebrowser: if rm.primitive == 'DELAYED_LOAD_ARCHIVE': + ob.empty_display_type = 'CUBE' rm.path_archive = self.properties.filepath + # try to get the bounding box from the RIB file + with open(rm.path_archive) as f: + for ln in f.readlines(): + if not ln.startswith('##bbox: '): + continue + tokens = ln.replace('##bbox: ', '').split(' ') + min_x, max_x, min_y, max_y, min_z, max_z = tokens + max_scale = max(max(float(max_x), float(max_y)), float(max_z)) + ob.empty_display_size = max_scale + break + elif rm.primitive == 'PROCEDURAL_RUN_PROGRAM': rm.runprogram_path = self.properties.filepath elif rm.primitive == 'DYNAMIC_LOAD_DSO': diff --git a/rman_render.py b/rman_render.py index 79966062..57a89406 100644 --- a/rman_render.py +++ b/rman_render.py @@ -24,6 +24,7 @@ from .rfb_utils import string_utils from .rfb_utils import display_utils from .rfb_utils import scene_utils +from .rfb_utils import transform_utils from .rfb_utils.prefs_utils import get_pref # config @@ -1041,7 +1042,9 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex rib_output = string_utils.expand_string(rib_path, frame=frame, asFilePath=True) - self.sg_scene.Render("rib " + rib_output + " -archive") + cmd = 'rib ' + rib_output + ' -archive' + cmd = cmd + ' -bbox ' + transform_utils.get_world_bounding_box(context.selected_objects) + self.sg_scene.Render(cmd) except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) self.stop_render(stop_draw_thread=False) @@ -1063,7 +1066,9 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex rib_output = string_utils.expand_string(rib_path, frame=bl_scene.frame_current, asFilePath=True) - self.sg_scene.Render("rib " + rib_output + " -archive") + cmd = 'rib ' + rib_output + ' -archive' + cmd = cmd + ' -bbox ' + transform_utils.get_world_bounding_box(context.selected_objects) + self.sg_scene.Render(cmd) except Exception as e: self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) rfb_log().debug("Export failed: %s" % str(e)) From 412b4410be14f1d49e889218e0b36f1b5c236558 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Feb 2022 16:01:50 -0800 Subject: [PATCH 056/278] Remove some unused variables from get_world_bounding_box --- rfb_utils/transform_utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rfb_utils/transform_utils.py b/rfb_utils/transform_utils.py index 51d28648..4a5b9df0 100644 --- a/rfb_utils/transform_utils.py +++ b/rfb_utils/transform_utils.py @@ -22,8 +22,6 @@ def convert_matrix4x4(m): return rman_mtx def get_world_bounding_box(selected_obs): - min_list = list() - max_list = list() min_vector = None max_vector = None From 4770f600f88ec73438c385803e5a56e3a2a19cb9 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 21 Feb 2022 10:43:10 -0800 Subject: [PATCH 057/278] Save some rotational matrices as a globals in the light draw code. --- rman_ui/rman_ui_light_handlers/__init__.py | 56 ++++++++++++---------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index 8a5c6307..a6308aea 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -447,6 +447,10 @@ (10, 20, 21) ] +__MTX_Y_180__ = Matrix.Rotation(math.radians(180.0), 4, 'Y') +__MTX_X_90__ = Matrix.Rotation(math.radians(90.0), 4, 'X') +__MTX_Y_90__ = Matrix.Rotation(math.radians(90.0), 4, 'Y') + _VERTEX_SHADER_UV_ = ''' uniform mat4 modelMatrix; uniform mat4 viewProjectionMatrix; @@ -533,8 +537,8 @@ def _get_sun_direction(ob): rm = light.renderman.get_light_node() m = Matrix.Identity(4) - m = m @ Matrix.Rotation(math.radians(90.0), 4, 'X') - m = m @ Matrix.Rotation(math.radians(90.0), 4, 'Y') + m = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') + m = m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') month = float(rm.month) day = float(rm.day) @@ -799,7 +803,7 @@ def draw_rect_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') box = [m @ Vector(pt) for pt in s_rmanLightLogo['box']] box_indices = _get_indices(s_rmanLightLogo['box']) @@ -840,7 +844,7 @@ def draw_rect_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') tex = '' col = None if light_shader_name == 'PxrRectLight': @@ -861,18 +865,18 @@ def draw_sphere_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') disk = [m @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = m @ Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') disk = [m2 @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = m @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') disk = [m3 @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -908,7 +912,7 @@ def draw_sphere_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ Matrix.Scale(0.5, 4) @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m = ob_matrix @ Matrix.Scale(0.5, 4) @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') idx_buffer = make_sphere_idx_buffer() if light_shader_name in ['PxrSphereLight']: col = light_shader.lightColor @@ -931,7 +935,7 @@ def draw_envday_light(ob): ob_matrix = m m = Matrix(ob_matrix) - m = m @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') west_rr_shape = [m @ Vector(pt) for pt in s_envday['west_rr_shape']] west_rr_indices = _get_indices(s_envday['west_rr_shape']) @@ -1006,7 +1010,7 @@ def draw_disk_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') disk = [m @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) @@ -1047,7 +1051,7 @@ def draw_disk_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') col = light_shader.lightColor draw_solid(ob, s_diskLight, m, col=col) @@ -1058,7 +1062,7 @@ def draw_dist_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') arrow1 = [m @ Vector(pt) for pt in s_distantLight['arrow1']] arrow1_indices = _get_indices(s_distantLight['arrow1']) @@ -1097,12 +1101,12 @@ def draw_portal_light(ob): R_inside_indices = _get_indices(s_rmanLightLogo['R_inside']) draw_line_shape(ob, _SHADER_, R_inside, R_inside_indices) - m = ob_matrix @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') arrow = [m @ Vector(pt) for pt in s_rmanLightLogo['arrow']] arrow_indices = _get_indices(s_rmanLightLogo['arrow']) draw_line_shape(ob, _SHADER_, arrow, arrow_indices) - m = ob_matrix @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m = ob_matrix @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') m = m @ Matrix.Scale(0.5, 4) rays = [m @ Vector(pt) for pt in s_portalRays] rays_indices = _get_indices(s_portalRays) @@ -1118,7 +1122,7 @@ def draw_dome_light(ob): scale = max(sca) # take the max axis m = Matrix.Rotation(angle, 4, axis) m = m @ Matrix.Scale(100 * scale, 4) - m = m @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m = m @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') sphere_pts = make_sphere() sphere = [m @ Vector(p) for p in sphere_pts] @@ -1235,7 +1239,7 @@ def draw_rod(ob, leftEdge, rightEdge, topEdge, bottomEdge, topEdge, bottomEdge, front, -back, m) - m = world_mat @ Matrix.Rotation(math.radians(-90.0), 4, 'X') + m = world_mat @ __MTX_X_90__ #Matrix.Rotation(math.radians(-90.0), 4, 'X') # top and bottom @@ -1244,7 +1248,7 @@ def draw_rod(ob, leftEdge, rightEdge, topEdge, bottomEdge, leftEdge, rightEdge, backEdge, frontEdge, top, -bottom, m) - m = world_mat @ Matrix.Rotation(math.radians(90.0), 4, 'Y') + m = world_mat @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') # left and right @@ -1258,7 +1262,7 @@ def draw_rod_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') light = ob.data rm = light.renderman.get_light_node() @@ -1350,7 +1354,7 @@ def draw_ramp_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') # begin begin_m = m @ Matrix.Scale(begin, 4) @@ -1359,11 +1363,11 @@ def draw_ramp_light_filter(ob): disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = begin_m @ Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = begin_m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') disk = [m2 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = begin_m @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = begin_m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') disk = [m3 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -1373,11 +1377,11 @@ def draw_ramp_light_filter(ob): disk = [end_m @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = end_m @ Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = end_m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') disk = [m2 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = end_m @ Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = end_m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') disk = [m3 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -1385,7 +1389,7 @@ def draw_ramp_light_filter(ob): elif rampType == 1: m = Matrix(ob.matrix_world) - m = m @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') box = [m @ Vector(pt) for pt in s_rmanLightLogo['box']] n = mathutils.geometry.normal(box) @@ -1410,7 +1414,7 @@ def draw_ramp_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') disk_indices = _get_indices(s_diskLight) if begin > 0.0: @@ -1432,7 +1436,7 @@ def draw_barn_light_filter(ob): _SHADER_.bind() m = Matrix(ob.matrix_world) - m = m @ Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') set_selection_color(ob) From 54a78a5fd67fef15de65d5c23c66c41a8a4d173b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 21 Feb 2022 11:32:11 -0800 Subject: [PATCH 058/278] Switch out the use of user_map when dealing with a ParticleSettings in rman_scene_sync. --- rman_scene_sync.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 89734754..c817231e 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -371,7 +371,7 @@ def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): portal.original.update_tag() - + @time_this def update_scene(self, context, depsgraph): ## FIXME: this function is waaayyy too big and is doing too much stuff @@ -457,10 +457,9 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.ParticleSettings): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) - users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): psys_translator = self.rman_scene.rman_translators['PARTICLES'] - for o in users[obj.id.original]: + for o in self.rman_scene.bl_scene.objects: psys = None ob = o.evaluated_get(depsgraph) for ps in ob.particle_systems: From eb1f06a426bd4103604d4af60dddaf4e4f734f5f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Feb 2022 11:28:28 -0800 Subject: [PATCH 059/278] When exporting an instance, add the rman_sg_group node to the instance's parent's instances list, not the prototype's list. --- rfb_utils/object_utils.py | 2 ++ rman_scene.py | 15 +++++--- rman_scene_sync.py | 72 ++++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 35 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index d8b629a6..a0f8ea74 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -196,6 +196,8 @@ def _detect_primitive_(ob): elif ob.type == 'CAMERA': return 'CAMERA' elif ob.type == 'EMPTY': + if ob.is_instancer: + return 'EMPTY_INSTANCER' return 'EMPTY' elif ob.type == 'GPENCIL': return 'GPENCIL' diff --git a/rman_scene.py b/rman_scene.py index f86b8b8c..c6bce2ff 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -148,6 +148,7 @@ def create_translators(self): self.rman_translators['HAIR'] = RmanHairTranslator(rman_scene=self) self.rman_translators['GROUP'] = RmanGroupTranslator(rman_scene=self) self.rman_translators['EMPTY'] = RmanEmptyTranslator(rman_scene=self) + self.rman_translators['EMPTY_INSTANCER'] = RmanEmptyTranslator(rman_scene=self) self.rman_translators['POINTS'] = RmanPointsTranslator(rman_scene=self) self.rman_translators['META'] = RmanBlobbyTranslator(rman_scene=self) self.rman_translators['PARTICLES'] = RmanParticlesTranslator(rman_scene=self) @@ -603,11 +604,11 @@ def export_data_blocks(self, selected_objects=False): ob_eval = ob.evaluated_get(self.depsgraph) psys = None - parent = None + instance_parent = None proto_key = object_utils.prototype_key(ob_inst) if ob_inst.is_instance: psys = ob_inst.particle_system - parent = ob_inst.parent + instance_parent = ob_inst.parent rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) @@ -640,7 +641,7 @@ def export_data_blocks(self, selected_objects=False): # Attach a material if psys: - self.attach_particle_material(psys.settings, parent, ob, rman_sg_group) + self.attach_particle_material(psys.settings, instance_parent, ob, rman_sg_group) rman_sg_group.bl_psys_settings = psys.settings.original else: self.attach_material(ob, rman_sg_group) @@ -654,7 +655,13 @@ def export_data_blocks(self, selected_objects=False): rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group + + if instance_parent: + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) + if rman_parent_node: + rman_parent_node.instances[group_db_name] = rman_sg_group + else: + rman_sg_node.instances[group_db_name] = rman_sg_group if rman_type == "META": # meta/blobbies are already in world space. Their instances don't need to diff --git a/rman_scene_sync.py b/rman_scene_sync.py index c817231e..0325602a 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -89,7 +89,7 @@ def scene_updated(self): self.rman_updates[o.original] = rman_update except: continue - self.check_all_instances = True + #self.check_all_instances = True self.rman_scene.num_objects_in_viewlayer = len(visible_objects) self.rman_scene.objects_in_viewlayer = [o for o in visible_objects] @@ -258,12 +258,6 @@ def check_particle_instancer(self, ob_update, psys): rman_update.is_updated_transform = ob_update.is_updated_transform self.rman_updates[col_obj.original] = rman_update - def check_particle_systems(self, ob_update): - ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) - for psys in ob.particle_systems: - if object_utils.is_particle_instancer(psys): - self.check_particle_instancer(ob_update, psys) - def update_particle_emitter(self, ob, psys): psys_translator = self.rman_scene.rman_translators['PARTICLES'] proto_key = object_utils.prototype_key(ob) @@ -391,7 +385,6 @@ def update_scene(self, context, depsgraph): if self.rman_scene.num_object_instances != len(depsgraph.object_instances): rfb_log().debug("\tNumber of instances changed: %d -> %d" % (self.rman_scene.num_object_instances, len(depsgraph.object_instances))) self.num_instances_changed = True - self.check_all_instances = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) for obj in reversed(depsgraph.updates): @@ -458,7 +451,6 @@ def update_scene(self, context, depsgraph): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - psys_translator = self.rman_scene.rman_translators['PARTICLES'] for o in self.rman_scene.bl_scene.objects: psys = None ob = o.evaluated_get(depsgraph) @@ -469,9 +461,18 @@ def update_scene(self, context, depsgraph): if not psys: continue if object_utils.is_particle_instancer(psys): - self.check_particle_instancer(obj, psys) + #self.check_particle_instancer(obj, psys) + pass else: self.update_particle_emitter(ob, psys) + + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[o.original] = rman_update elif isinstance(obj.id, bpy.types.ShaderNodeTree): if obj.id.name in bpy.data.node_groups: @@ -520,8 +521,6 @@ def update_scene(self, context, depsgraph): rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) self.rman_updates[obj.id.original] = rman_update - self.check_particle_systems(obj) - # Check if this object is the focus object the camera. If it is # we need to update the camera if obj.is_updated_transform: @@ -538,6 +537,13 @@ def update_scene(self, context, depsgraph): else: pass #self.update_geometry_node_instances(obj.id) + + if not self.rman_updates and self.num_instances_changed: + # The number of instances changed, but we are not able + # to determine what changed. We are forced to check + # all instances. + rfb_log().debug("Set check_all_instances to True") + self.check_all_instances = True if self.check_all_instances or self.rman_updates: self.check_instances() @@ -560,14 +566,14 @@ def check_instances(self): ob_key = instance.object.original ob_eval = instance.object.evaluated_get(self.rman_scene.depsgraph) - parent = None + instance_parent = None psys = None is_new_object = False proto_key = object_utils.prototype_key(instance) if instance.is_instance: ob_key = instance.instance_object.original psys = instance.particle_system - parent = instance.parent + instance_parent = instance.parent if proto_key in deleted_obj_keys: deleted_obj_keys.remove(proto_key) @@ -575,6 +581,7 @@ def check_instances(self): rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) + rman_parent_node = None if rman_sg_node and rman_type != rman_sg_node.rman_type: # Types don't match # @@ -610,6 +617,7 @@ def check_instances(self): rman_update.is_updated_transform = True self.rman_updates[ob_key] = rman_update rman_update.is_updated_geometry = False + clear_instances.append(rman_sg_node) if self.check_all_instances: # check all instances in the scene @@ -621,22 +629,15 @@ def check_instances(self): self.rman_updates[ob_key] = rman_update else: if ob_key not in self.rman_updates: - if not parent: + if not instance_parent: continue - # check if the parent is also marked to be updated - if parent.original not in self.rman_updates: + if instance_parent.original not in self.rman_updates: continue - - # parent was marked needing update. - # create an on the fly RmanUpdate() - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update + rman_update = self.rman_updates[instance_parent.original] else: rman_update = self.rman_updates[ob_key] - if rman_sg_node and not is_new_object: + if rman_sg_node and not is_new_object and not instance.is_instance: if rman_update.is_updated_geometry and proto_key not in already_udpated: translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) @@ -654,9 +655,16 @@ def check_instances(self): # of them, as they are part of lights continue - if rman_sg_node not in clear_instances: - # clear all instances for this prototype, if - # we have not already done so + # clear all instances for this prototype, if + # we have not already done so + if instance_parent: + parent_proto_key = object_utils.prototype_key(instance_parent) + rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) + if rman_parent_node and rman_parent_node not in clear_instances: + rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) + rman_parent_node.clear_instances() + clear_instances.append(rman_parent_node) + elif rman_sg_node not in clear_instances: rfb_log().debug("\tClearing instances: %s" % proto_key) rman_sg_node.clear_instances() clear_instances.append(rman_sg_node) @@ -671,7 +679,6 @@ def check_instances(self): if not rman_sg_group: rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - rman_sg_node.instances[group_db_name] = rman_sg_group if object_utils.has_empty_parent(ob_eval): # this object is a child of an empty. Add it to the empty. @@ -696,7 +703,7 @@ def check_instances(self): # Attach a material if psys: - self.rman_scene.attach_particle_material(psys.settings, parent, ob_eval, rman_sg_group) + self.rman_scene.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) rman_sg_group.bl_psys_settings = psys.settings.original else: self.rman_scene.attach_material(ob_eval, rman_sg_group) @@ -708,7 +715,10 @@ def check_instances(self): translator.export_object_attributes(ob_eval, rman_sg_group) translator.export_object_id(ob_eval, rman_sg_group, instance) - rman_sg_node.instances[group_db_name] = rman_sg_group + if rman_parent_node: + rman_parent_node.instances[group_db_name] = rman_sg_group + else: + rman_sg_node.instances[group_db_name] = rman_sg_group if rman_sg_node.rman_sg_particle_group_node: rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) From ab588c172587a3950a309322e2396463f9cf5cef Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Feb 2022 12:53:33 -0800 Subject: [PATCH 060/278] * Update export_instances_motion to deal with instances changes * Make sure not to addd instances of EMPTY, EMPTY_INSTANCER and LIGHTFILTER --- rfb_utils/object_utils.py | 3 +++ rman_scene.py | 48 +++++++++++++++++++-------------------- rman_scene_sync.py | 9 +++----- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index a0f8ea74..f366cf4d 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -3,6 +3,9 @@ from .prefs_utils import get_pref from . import string_utils +# These types don't create instances +_RMAN_NO_INSTANCES_ = ['EMPTY', 'EMPTY_INSTANCER', 'LIGHTFILTER'] + def get_db_name(ob, rman_type='', psys=None): db_name = '' diff --git a/rman_scene.py b/rman_scene.py index c6bce2ff..60fdb692 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -605,7 +605,7 @@ def export_data_blocks(self, selected_objects=False): ob_eval = ob.evaluated_get(self.depsgraph) psys = None instance_parent = None - proto_key = object_utils.prototype_key(ob_inst) + proto_key = object_utils.prototype_key(ob_inst) if ob_inst.is_instance: psys = ob_inst.particle_system instance_parent = ob_inst.parent @@ -618,11 +618,8 @@ def export_data_blocks(self, selected_objects=False): if rman_type == 'LIGHT': self.check_solo_light(rman_sg_node, ob_eval) - if rman_type == 'LIGHTFILTER': - # we don't need to create instances of light filters - # we just need them to be added as coordinate systems - # which should have been done in export - continue + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_group_translator.export(ob, group_db_name) @@ -761,7 +758,7 @@ def export_data_block(self, proto_key, ob): return rman_sg_node - def export_instances_motion(self, selected_objects=list()): + def export_instances_motion(self, selected_objects=False): origframe = self.bl_scene.frame_current mb_segs = self.bl_scene.renderman.motion_segments @@ -795,31 +792,29 @@ def export_instances_motion(self, selected_objects=list()): break cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) - for i, ob_inst in enumerate(self.depsgraph.object_instances): - if selected_objects: - if objFound: - break - - if ob_inst.is_instance: - if ob_inst.instance_object.name == selected_objects: - objFound = True - elif ob_inst.object.name == selected_objects.name: - objFound = True + rfb_log().debug(" Export Sample: %i" % samp) + for i, ob_inst in enumerate(self.depsgraph.object_instances): + if selected_objects and not self.is_instance_selected(ob_inst): + continue - if not objFound: - continue - - if not ob_inst.show_self: + if not self.check_visibility(ob_inst): continue psys = None ob = ob_inst.object.evaluated_get(self.depsgraph) proto_key = object_utils.prototype_key(ob_inst) rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) - self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) + self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) + instance_parent = None + rman_parent_node = None if ob_inst.is_instance: - proto_key = ob_inst.instance_object.data.original psys = ob_inst.particle_system + instance_parent = ob_inst.parent + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent)) + + rman_type = object_utils._detect_primitive_(ob) + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue # check particles for motion for psys in ob.particle_systems: @@ -842,7 +837,7 @@ def export_instances_motion(self, selected_objects=list()): if ob.name_full not in self.moving_objects and not psys: continue - rman_sg_node = self.rman_prototypes.get(proto_key, None) + rman_sg_node = self.get_rman_prototype(proto_key, ob=ob) if not rman_sg_node: continue @@ -856,7 +851,10 @@ def export_instances_motion(self, selected_objects=list()): if rman_sg_node.is_transforming or psys: group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if instance_parent: + rman_sg_group = rman_parent_node.instances.get(group_db_name, None) + else: + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) if rman_sg_group: if first_sample: rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 0325602a..3cf5a682 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -647,13 +647,10 @@ def check_instances(self): already_udpated.append(proto_key) if rman_type == 'EMPTY': - self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) - continue + self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) - elif rman_type == 'LIGHTFILTER': - # Light filters are special. We don't need to add instances - # of them, as they are part of lights - continue + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue # clear all instances for this prototype, if # we have not already done so From 303bf29ffef5f5d4966f806d05f9788701e02765 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Feb 2022 13:49:23 -0800 Subject: [PATCH 061/278] Add an extra parameter to export_data_blocks to indicate what objects should be exported. We need this for baking. --- rman_scene.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 60fdb692..84de7f53 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -429,9 +429,9 @@ def export_bake_brickmap_selected(self): ob = self.context.active_object self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - objects_needed = [x for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] - objects_needed.append(ob) - self.export_data_blocks(selected_objects=objects_needed) + objects_needed = [x.original for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] + objects_needed.append(ob.original) + self.export_data_blocks(objects_list=objects_needed) self.export_samplefilters() self.export_displayfilters() @@ -585,7 +585,7 @@ def is_instance_selected(self, instance): return True - def export_data_blocks(self, selected_objects=False): + def export_data_blocks(self, selected_objects=False, objects_list=False): rman_group_translator = self.rman_translators['GROUP'] total = len(self.depsgraph.object_instances) for i, ob_inst in enumerate(self.depsgraph.object_instances): @@ -598,6 +598,10 @@ def export_data_blocks(self, selected_objects=False): if selected_objects and not self.is_instance_selected(ob_inst): continue + # only export these objects + if objects_list and ob.original not in objects_list: + continue + if not self.check_visibility(ob_inst): rfb_log().debug(" Object (%s) not visible" % (ob.name)) continue From 9725c9b042f04060c19f3ca957dba8137c8daaae Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Feb 2022 14:35:37 -0800 Subject: [PATCH 062/278] Save a reference to the viewport in RmanRender. --- rman_render.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/rman_render.py b/rman_render.py index 57a89406..04d5f54c 100644 --- a/rman_render.py +++ b/rman_render.py @@ -39,20 +39,6 @@ __DRAW_THREAD__ = None __RMAN_STATS_THREAD__ = None -def __turn_off_viewport__(): - ''' - Loop through all of the windows/areas and turn shading to SOLID - for all view_3d areas. - ''' - rfb_log().debug("Attempting to turn off viewport render") - for window in bpy.context.window_manager.windows: - for area in window.screen.areas: - if area.type == 'VIEW_3D': - for space in area.spaces: - if space.type == 'VIEW_3D': - space.shading.type = 'SOLID' - area.tag_redraw() - def __update_areas__(): for window in bpy.context.window_manager.windows: for area in window.screen.areas: @@ -93,7 +79,7 @@ def stopRender(self): global __RMAN_RENDER__ rfb_log().debug("Stop Render Requested.") if __RMAN_RENDER__.rman_interactive_running: - __turn_off_viewport__() + __RMAN_RENDER__.bl_viewport.shading.type = 'SOLID' __RMAN_RENDER__.del_bl_engine() def selectObjectById(self): @@ -156,8 +142,8 @@ def start_cmd_server(): def draw_threading_func(db): refresh_rate = get_pref('rman_viewport_refresh_rate', default=0.01) while db.rman_is_live_rendering: - if not scene_utils.any_areas_shading(): - # if there are no 3d viewports, stop IPR + if db.bl_viewport.shading.type != 'RENDERED': + # if the viewport is not rendering, stop IPR db.del_bl_engine() break if db.rman_is_xpu: @@ -288,6 +274,7 @@ def __init__(self): self.stats_mgr = RfBStatsManager(self) self.deleting_bl_engine = threading.Lock() self.stop_render_mtx = threading.Lock() + self.bl_viewport = None self._start_prman_begin() @@ -449,6 +436,7 @@ def reset(self): self.rman_license_failed_message = '' self.rman_is_xpu = False self.rman_is_refining = False + self.bl_viewport = None def start_render(self, depsgraph, for_background=False): @@ -891,6 +879,7 @@ def start_interactive_render(self, context, depsgraph): self.it_port = start_cmd_server() render_into_org = '' self.rman_render_into = rm.render_ipr_into + self.bl_viewport = context.space_data self.rman_callbacks.clear() # register the blender display driver From d6c1c104f998d9d8db63cf12df3a5b58ba56391f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Feb 2022 15:55:21 -0800 Subject: [PATCH 063/278] * Add a ScopeEdit inside of the RmanSgNode destructor. For some reason the destructor can be called outside of the Scope, in some cases. * When dealing with instances in check_visibility, we need to check both the instanced object, and the parent. --- rman_scene.py | 13 +++++++------ rman_sg_nodes/rman_sg_node.py | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 84de7f53..a524566b 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -558,13 +558,14 @@ def check_visibility(self, instance): if not self.is_interactive: return True viewport = self.context.space_data - ob_eval = instance.object.evaluated_get(self.depsgraph) - ''' + if instance.is_instance: - ob_eval = instance.instance_object.original.evaluated_get(self.depsgraph) - if instance.parent: - ob_eval = instance.parent - ''' + ob_eval = instance.instance_object + ob_eval_visible = ob_eval.visible_in_viewport_get(viewport) + parent_visible = instance.parent.visible_in_viewport_get(viewport) + return (ob_eval_visible or parent_visible) + + ob_eval = instance.object.evaluated_get(self.depsgraph) visible = ob_eval.visible_in_viewport_get(viewport) return visible diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index c2b005ed..be2b0fd2 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -58,12 +58,13 @@ def __init__(self, rman_scene, sg_node, db_name): def __del__(self): if self.rman_scene.rman_render.rman_running and self.rman_scene.rman_render.sg_scene: - #rfb_log().debug("Deleting dag node: %s" % self.db_name) - self.instances.clear() - if isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Group): - self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) - elif isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Material): - self.rman_scene.sg_scene.DeleteMaterial(self.sg_node) + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + #rfb_log().debug("Deleting dag node: %s" % self.db_name) + self.instances.clear() + if isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Group): + self.rman_scene.sg_scene.DeleteDagNode(self.sg_node) + elif isinstance(self.sg_node, self.rman_scene.rman.scenegraph.Material): + self.rman_scene.sg_scene.DeleteMaterial(self.sg_node) def clear_instances(self): if self.rman_scene.rman_render.rman_running: From baa9326fa95de92af439accf2e6e715df3ff1f71 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 24 Feb 2022 10:52:31 -0800 Subject: [PATCH 064/278] Encapsulate some duplicate code regarding sg_group node creation/editing into a export_instance function. --- rman_scene.py | 101 ++++++++++++++++++++++++--------------------- rman_scene_sync.py | 51 +---------------------- 2 files changed, 57 insertions(+), 95 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index a7328c23..1579c476 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -585,9 +585,62 @@ def is_instance_selected(self, instance): return False return True + + def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): + rman_group_translator = self.rman_translators['GROUP'] + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + + # Object attrs + translator = self.rman_translators.get(rman_type, None) + if translator: + translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_id(ob_eval, rman_sg_group, ob_inst) + + # Add any particles necessary + if rman_sg_node.rman_sg_particle_group_node: + if (len(ob_eval.particle_systems) > 0) and ob_inst.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + + # Attach a material + if psys: + self.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.attach_material(ob_eval, rman_sg_group) + + if object_utils.has_empty_parent(ob_eval): + # this object is a child of an empty. Add it to the empty. + ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) + parent_proto_key = object_utils.prototype_key(ob_eval.parent) + rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + else: + self.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + if instance_parent: + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) + if rman_parent_node: + if group_db_name in rman_parent_node.instances: + del rman_parent_node[group_db_name] + rman_parent_node.instances[group_db_name] = rman_sg_group + else: + if group_db_name in rman_sg_node.instances: + del rman_sg_node[group_db_name] + rman_sg_node.instances[group_db_name] = rman_sg_group + + if rman_type == "META": + # meta/blobbies are already in world space. Their instances don't need to + # set a transform. + return rman_sg_group + + rman_group_translator.update_transform(ob_inst, rman_sg_group) + return rman_sg_group + def export_data_blocks(self, selected_objects=False, objects_list=False): - rman_group_translator = self.rman_translators['GROUP'] total = len(self.depsgraph.object_instances) for i, ob_inst in enumerate(self.depsgraph.object_instances): ob = ob_inst.object @@ -625,52 +678,8 @@ def export_data_blocks(self, selected_objects=False, objects_list=False): if rman_type in object_utils._RMAN_NO_INSTANCES_: continue - - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_group_translator.export(ob, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - - # Object attrs - translator = self.rman_translators.get(rman_type, None) - if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) - translator.export_object_id(ob_eval, rman_sg_group, ob_inst) - - # Add any particles necessary - if rman_sg_node.rman_sg_particle_group_node: - if (len(ob.particle_systems) > 0) and ob_inst.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - # Attach a material - if psys: - self.attach_particle_material(psys.settings, instance_parent, ob, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original - else: - self.attach_material(ob, rman_sg_group) - - if object_utils.has_empty_parent(ob): - # this object is a child of an empty. Add it to the empty. - ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) - parent_proto_key = object_utils.prototype_key(ob.parent) - rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) - else: - self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - - if instance_parent: - rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) - if rman_parent_node: - rman_parent_node.instances[group_db_name] = rman_sg_group - else: - rman_sg_node.instances[group_db_name] = rman_sg_group - if rman_type == "META": - # meta/blobbies are already in world space. Their instances don't need to - # set a transform. - return - - rman_group_translator.update_transform(ob_inst, rman_sg_group) + self.export_instance(ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys) def export_data_block(self, proto_key, ob): rman_type = object_utils._detect_primitive_(ob) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 3cf5a682..0982dc6f 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -559,7 +559,6 @@ def check_instances(self): clear_instances = list() # list of objects who had their instances cleared rfb_log().debug("Updating instances") with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - rman_group_translator = self.rman_scene.rman_translators['GROUP'] for instance in self.rman_scene.depsgraph.object_instances: if instance.object.type in ('ARMATURE', 'CAMERA'): continue @@ -581,7 +580,6 @@ def check_instances(self): rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) - rman_parent_node = None if rman_sg_node and rman_type != rman_sg_node.rman_type: # Types don't match # @@ -671,21 +669,7 @@ def check_instances(self): # add an instance of it continue - group_db_name = object_utils.get_group_db_name(instance) - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if not rman_sg_group: - rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - - if object_utils.has_empty_parent(ob_eval): - # this object is a child of an empty. Add it to the empty. - ob_parent_eval = ob_eval.parent.evaluated_get(self.rman_scene.depsgraph) - parent_proto_key = object_utils.prototype_key(ob_eval.parent) - rman_empty_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) - else: - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) if rman_type == 'LIGHT': # We are dealing with a light. Check if it's a solo light, or muted @@ -696,31 +680,7 @@ def check_instances(self): # Hide the default light if self.rman_scene.default_light.GetHidden() != 1: - self.rman_scene.default_light.SetHidden(1) - - # Attach a material - if psys: - self.rman_scene.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original - else: - self.rman_scene.attach_material(ob_eval, rman_sg_group) - self.rman_scene.get_root_sg_node().AddChild(rman_sg_group.sg_node) - - # Object attrs - translator = self.rman_scene.rman_translators.get(rman_type, None) - if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) - translator.export_object_id(ob_eval, rman_sg_group, instance) - - if rman_parent_node: - rman_parent_node.instances[group_db_name] = rman_sg_group - else: - rman_sg_node.instances[group_db_name] = rman_sg_group - - if rman_sg_node.rman_sg_particle_group_node: - rman_sg_node.sg_node.RemoveChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - if (len(ob_eval.particle_systems) > 0) and instance.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + self.rman_scene.default_light.SetHidden(1) # Delete any removed partcle systems if proto_key in self.rman_scene.rman_particles: @@ -735,12 +695,6 @@ def check_instances(self): rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) for k in rman_particle_nodes: del ob_psys[k] - - if rman_type == 'META': - continue - - # Transform - rman_group_translator.update_transform(instance, rman_sg_group) # delete objects if deleted_obj_keys: @@ -757,7 +711,6 @@ def delete_objects(self, deleted_obj_keys=list()): rfb_log().debug("\tDeleting: %s" % rman_sg_node.db_name) if key in self.rman_scene.rman_particles: rfb_log().debug("\t\tRemoving particles...") - ob_psys = self.rman_scene.rman_particles[key] del self.rman_scene.rman_particles[key] del self.rman_scene.rman_prototypes[key] From 6d06a14691778955905b87cd9f278060c023e404 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Feb 2022 08:24:49 -0800 Subject: [PATCH 065/278] When checking ShaderNodeTree updates, check if the name contains our fake nodegroup name. --- rman_bl_nodes/__init__.py | 3 ++- rman_constants.py | 1 + rman_scene_sync.py | 36 ++++-------------------------------- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index 22840da6..09bd8ccc 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -16,6 +16,7 @@ from ..rman_properties import rman_properties_camera from ..rman_constants import RFB_ARRAYS_MAX_LEN from ..rman_constants import CYCLES_NODE_MAP +from ..rman_constants import RMAN_FAKE_NODEGROUP from nodeitems_utils import NodeCategory, NodeItem from collections import OrderedDict from bpy.props import * @@ -362,7 +363,7 @@ def init(self, context): if color_rman_ramps or float_rman_ramps: node_group = bpy.data.node_groups.new( - '.__RMAN_FAKE_NODEGROUP__', 'ShaderNodeTree') + RMAN_FAKE_NODEGROUP, 'ShaderNodeTree') node_group.use_fake_user = True self.rman_fake_node_group_ptr = node_group self.rman_fake_node_group = node_group.name diff --git a/rman_constants.py b/rman_constants.py index 664456d6..fad51f2b 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -40,6 +40,7 @@ RFB_VIEWPORT_MAX_BUCKETS = 10 RFB_PREFS_NAME = __name__.split('.')[0] RMAN_RENDERMAN_BLUE = (0.0, 0.498, 1.0, 1.0) +RMAN_FAKE_NODEGROUP = '.__RMAN_FAKE_NODEGROUP__' RFB_FLOAT3 = ['color', 'point', 'vector', 'normal'] RFB_FLOATX = ['color', 'point', 'vector', 'normal', 'matrix'] diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 0982dc6f..73750fb9 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -332,35 +332,6 @@ def update_collection(self, coll): rman_update.is_updated_transform = True self.rman_updates[o.original] = rman_update - def update_geometry_node_instances(self, obj): - def update_geo_instances(nodes): - # look for all point instance nodes - for n in [node for node in nodes if isinstance(node, bpy.types.GeometryNodePointInstance)]: - if n.instance_type == 'OBJECT': - instance_obj = n.inputs['Object'].default_value - if instance_obj: - self.clear_instances(instance_obj) - self.update_particles.add(instance_obj) - self.update_instances.add(instance_obj.original) - elif n.instance_type == 'COLLECTION': - instance_coll = n.inputs['Collection'].default_value - if instance_coll: - self.update_collection(instance_coll) - - - if rman_constants.BLENDER_VERSION_MAJOR >= 2 and rman_constants.BLENDER_VERSION_MINOR >= 92: - if isinstance(obj, bpy.types.GeometryNodeTree): - rfb_log().debug("Geometry Node Tree updated: %s" % obj.name) - # look for all point instance nodes - update_geo_instances(obj.nodes) - elif hasattr(obj, 'modifiers'): - # This is an object with modifiers. Look for any geometry node trees attached. - node_tree = None - for modifier in obj.modifiers: - if modifier.type == 'NODES': - rfb_log().debug("Geometry Node Tree updated: %s" % modifier.node_group.name) - update_geo_instances(modifier.node_group.nodes) - def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): portal.original.update_tag() @@ -478,7 +449,9 @@ def update_scene(self, context, depsgraph): if obj.id.name in bpy.data.node_groups: if len(obj.id.nodes) < 1: continue - # this is probably one of our fake node groups with ramps + if not obj.id.name.startswith(rman_constants.RMAN_FAKE_NODEGROUP): + continue + # this is one of our fake node groups with ramps # update all of the users of this node tree rfb_log().debug("ShaderNodeTree updated: %s" % obj.id.name) users = context.blend_data.user_map(subset={obj.id.original}) @@ -535,8 +508,7 @@ def update_scene(self, context, depsgraph): #self.update_collection(obj.id) else: - pass - #self.update_geometry_node_instances(obj.id) + rfb_log().debug("Not handling %s update: %s" % (str(type(obj.id), obj.id.name))) if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able From ae88a0e8a66988558f188cb3f13fc5630b7a971b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Feb 2022 08:52:55 -0800 Subject: [PATCH 066/278] Fix typos in a debug print --- rman_scene_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 73750fb9..24aec337 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -508,7 +508,7 @@ def update_scene(self, context, depsgraph): #self.update_collection(obj.id) else: - rfb_log().debug("Not handling %s update: %s" % (str(type(obj.id), obj.id.name))) + rfb_log().debug("Not handling %s update: %s" % (str(type(obj.id)), obj.id.name)) if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able From cf8dea000372a6ee8e0526b6c44a3dc14e263343 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Feb 2022 09:48:55 -0800 Subject: [PATCH 067/278] Some small tweaks to the object ID encoding --- rfb_utils/object_utils.py | 5 +++-- rman_translators/rman_translator.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index f366cf4d..89cf254d 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -45,10 +45,11 @@ def get_group_db_name(ob_inst): ob = ob_inst.instance_object parent = ob_inst.parent psys = ob_inst.particle_system + persistent_id = "%d%d" % (ob_inst.persistent_id[1], ob_inst.persistent_id[0]) if psys: - group_db_name = "%s|%s|%s|%d|%d" % (parent.name_full, ob.name_full, psys.name, ob_inst.persistent_id[1], ob_inst.persistent_id[0]) + group_db_name = "%s|%s|%s|%s" % (parent.name_full, ob.name_full, psys.name, persistent_id) else: - group_db_name = "%s|%s|%d|%d" % (parent.name_full, ob.name_full, ob_inst.persistent_id[1], ob_inst.persistent_id[0]) + group_db_name = "%s|%s|%s" % (parent.name_full, ob.name_full, persistent_id) else: ob = ob_inst.object group_db_name = "%s" % (ob.name_full) diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 1be0a613..c7312e49 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -113,14 +113,16 @@ def export_object_id(self, ob, rman_sg_node, ob_inst): if not rman_sg_node.sg_node: return name = ob.name_full + if ob_inst.is_instance: + name = ob_inst.parent.name attrs = rman_sg_node.sg_node.GetAttributes() rman_type = object_utils._detect_primitive_(ob) # Add ID if name != "": - persistent_id = ob_inst.persistent_id[0] + persistent_id = ob_inst.persistent_id[1] if persistent_id == 0: - persistent_id = int(hashlib.sha1(ob.name_full.encode()).hexdigest(), 16) % 10**8 + persistent_id = int(hashlib.sha1(name.encode()).hexdigest(), 16) % 10**8 self.rman_scene.obj_hash[persistent_id] = name attrs.SetInteger(self.rman_scene.rman.Tokens.Rix.k_identifier_id, persistent_id) From e406100c9ba8cbbb1c45392e6a6b380a1a636b24 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Feb 2022 15:45:42 -0800 Subject: [PATCH 068/278] Allow for instances from empties to override attributes and material bindings. --- rfb_utils/object_utils.py | 2 ++ rman_scene.py | 13 ++++++++++--- rman_translators/rman_translator.py | 8 +++++--- rman_ui/rman_ui_object_panels.py | 4 ---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 89cf254d..4cbcb9b0 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -169,6 +169,8 @@ def prototype_key(ob): return ob.original.name def _detect_primitive_(ob): + if ob is None: + return '' if isinstance(ob, bpy.types.ParticleSystem): return ob.settings.type diff --git a/rman_scene.py b/rman_scene.py index 1579c476..47f6a367 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -591,11 +591,16 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + is_empty_instancer = False + if instance_parent and object_utils._detect_primitive_(instance_parent) == 'EMPTY_INSTANCER': + is_empty_instancer = True # Object attrs translator = self.rman_translators.get(rman_type, None) if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) + translator.export_object_attributes(ob_eval, rman_sg_group) + if is_empty_instancer: + translator.export_object_attributes(instance_parent, rman_sg_group, remove=False) translator.export_object_id(ob_eval, rman_sg_group, ob_inst) # Add any particles necessary @@ -603,8 +608,10 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa if (len(ob_eval.particle_systems) > 0) and ob_inst.show_particles: rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - # Attach a material - if psys: + # Attach a material + if is_empty_instancer and instance_parent.renderman.rman_material_override: + self.attach_material(instance_parent, rman_sg_group) + elif psys: self.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) rman_sg_group.bl_psys_settings = psys.settings.original else: diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index c7312e49..6e6c97e4 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -197,7 +197,7 @@ def export_light_linking_attributes(self, ob, attrs): else: attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lightfilter_subset, '') - def export_object_attributes(self, ob, rman_sg_node): + def export_object_attributes(self, ob, rman_sg_node, remove=True): if not rman_sg_node.sg_node: return @@ -217,10 +217,12 @@ def export_object_attributes(self, ob, rman_sg_node): if isinstance(cond, str): node = rm if exec(cond): - attrs.Remove(ri_name) + if remove: + attrs.Remove(ri_name) continue elif float(val) == cond: - attrs.Remove(ri_name) + if remove: + attrs.Remove(ri_name) continue is_array = False diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index bf980409..c4e5bcf5 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -151,8 +151,6 @@ def poll(cls, context): ob = context.object if ob.type != 'EMPTY': return False - if ob.is_instancer: - return False return (context.object and rd.engine in {'PRMAN_RENDER'} ) def draw(self, context): @@ -177,8 +175,6 @@ def poll(cls, context): ob = context.object if ob.type != 'EMPTY': return False - if ob.is_instancer: - return False mat = context.object.renderman.rman_material_override if not mat: return False From 8165db85da08581cd7d40b40d04295e86dbe73e2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Mar 2022 15:24:59 -0800 Subject: [PATCH 069/278] Address the complaint that ipr to it causes the viewport to go all grey. * we add a new code path for starting ipr to it * when we ipr to it, we no longer switch the viewport to render mode, so the viewport will keep the gl shading * we add a new depsgraph post and frame change post handlers to deal with changes to the scene, but only when we ipr to it * move the ipr optix denoiser option to the viewport options menu --- __init__.py | 5 ++++ rfb_utils/display_utils.py | 2 +- rman_config/config/rman_properties_scene.json | 6 ++-- rman_handlers/__init__.py | 29 +++++++++++++++++-- rman_operators/rman_operators_render.py | 29 ++++++++++++++++--- rman_render.py | 7 +++-- rman_scene.py | 6 +++- rman_ui/rman_ui_view3d_menus.py | 6 +++- rman_ui/rman_ui_view3d_panels.py | 18 ++---------- rman_ui/rman_ui_viewport.py | 8 +++-- 10 files changed, 84 insertions(+), 32 deletions(-) diff --git a/__init__.py b/__init__.py index 9338df54..46198a8a 100644 --- a/__init__.py +++ b/__init__.py @@ -107,9 +107,14 @@ def view_update(self, context, depsgraph): if self.export_failed: return + if self.rman_render.is_ipr_to_it(): + # if we are already IPRing to "it", stop the render + self.rman_render.stop_render(stop_draw_thread=False) + # if interactive rendering has not started, start it if not self.rman_render.rman_interactive_running and self.rman_render.sg_scene is None: self.rman_render.bl_engine = self + self.rman_render.rman_scene.ipr_render_into = 'blender' if not self.rman_render.start_interactive_render(context, depsgraph): self.export_failed = True return diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 934afebc..2c0e8048 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -620,7 +620,7 @@ def get_dspy_dict(rman_scene, expandTokens=True): do_optix_denoise = False if rman_scene.is_interactive: - display_driver = rm.render_ipr_into + display_driver = rman_scene.ipr_render_into do_optix_denoise = rm.blender_ipr_optix_denoiser elif (not rman_scene.external_render): diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 632b07f2..8271439a 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -79,7 +79,7 @@ } }, { - "panel": "RENDER_PT_renderman_render", + "panel": "", "page": "Display", "name": "render_ipr_into", "label": "Render IPR to", @@ -90,14 +90,14 @@ "help": "Controls where IPR renders will go to." }, { - "panel": "RENDER_PT_renderman_render", + "panel": "", "page": "Display", "name": "blender_ipr_optix_denoiser", "label": "Use Optix Denoiser", "type": "int", "default": 0, "widget": "checkbox", - "help": "Denoise IPR with OptiX denoiser", + "help": "Denoise viewport renders with OptiX denoiser", "update_function_name": "update_blender_ipr_optix_denoiser", "update_function": "def update_blender_ipr_optix_denoiser(self, context):\n from ..rman_render import RmanRender\n rr = RmanRender.get_rman_render()\n rr.rman_scene_sync.update_displays(context)", "conditionalVisOps": { diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index 5e2a9986..7b5904c1 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -3,6 +3,7 @@ from ..rfb_utils import shadergraph_utils from ..rfb_utils import upgrade_utils from ..rman_ui import rman_ui_light_handlers +from ..rman_render import RmanRender from bpy.app.handlers import persistent import bpy @@ -25,7 +26,25 @@ def rman_save_post(bl_scene): @persistent def depsgraph_update_post(bl_scene, depsgraph): - texture_utils.depsgraph_handler(bl_scene, depsgraph) + texture_utils.depsgraph_handler(bl_scene, depsgraph) + rman_render = RmanRender.get_rman_render() + + # check updates if we are render ipring into it + if rman_render.is_ipr_to_it(): + context = bpy.context + rman_render.update_scene(context, depsgraph) + rman_render.update_view(context, depsgraph) + +@persistent +def frame_change_post(bl_scene): + rman_render = RmanRender.get_rman_render() + # check updates if we are render ipring into it + if rman_render.is_ipr_to_it(): + context = bpy.context + depsgraph = context.evaluated_depsgraph_get() + rman_render.update_scene(context, depsgraph) + rman_render.update_view(context, depsgraph) + def register(): @@ -45,6 +64,9 @@ def register(): if depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: bpy.app.handlers.depsgraph_update_post.append(depsgraph_update_post) + if frame_change_post not in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.append(frame_change_post) + def unregister(): if rman_load_post in bpy.app.handlers.load_post: @@ -57,4 +79,7 @@ def unregister(): bpy.app.handlers.save_post.remove(rman_save_post) if depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update_post) + bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update_post) + + if frame_change_post in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.remove(frame_change_post) diff --git a/rman_operators/rman_operators_render.py b/rman_operators/rman_operators_render.py index fd4769bd..94c64049 100644 --- a/rman_operators/rman_operators_render.py +++ b/rman_operators/rman_operators_render.py @@ -195,10 +195,27 @@ class PRMAN_OT_StartInteractive(bpy.types.Operator): bl_description = "Start Interactive Rendering" bl_options = {'INTERNAL'} + render_to_it: bpy.props.BoolProperty(default=False) + + @classmethod + def description(cls, context, properties): + if properties.render_to_it: + return "Start IPR and render to 'it'" + return cls.bl_description + def invoke(self, context, event=None): - view = context.space_data - if view and view.shading.type != 'RENDERED': - view.shading.type = 'RENDERED' + scene = context.scene + if self.render_to_it: + rr = RmanRender.get_rman_render() + rr.rman_scene.ipr_render_into = 'it' + depsgraph = context.evaluated_depsgraph_get() + rr.start_interactive_render(context, depsgraph) + else: + view = context.space_data + if view and view.shading.type != 'RENDERED': + rr = RmanRender.get_rman_render() + rr.rman_scene.ipr_render_into = 'blender' + view.shading.type = 'RENDERED' return {'FINISHED'} @@ -211,7 +228,11 @@ class PRMAN_OT_StopInteractive(bpy.types.Operator): bl_options = {'INTERNAL'} def invoke(self, context, event=None): - if context.space_data.type == 'VIEW_3D': + scene = context.scene + rr = RmanRender.get_rman_render() + if rr.is_ipr_to_it(): + rr.stop_render(stop_draw_thread=False) + elif context.space_data.type == 'VIEW_3D': context.space_data.shading.type = 'SOLID' else: for window in bpy.context.window_manager.windows: diff --git a/rman_render.py b/rman_render.py index 04d5f54c..0d8c9f3b 100644 --- a/rman_render.py +++ b/rman_render.py @@ -79,7 +79,7 @@ def stopRender(self): global __RMAN_RENDER__ rfb_log().debug("Stop Render Requested.") if __RMAN_RENDER__.rman_interactive_running: - __RMAN_RENDER__.bl_viewport.shading.type = 'SOLID' + __RMAN_RENDER__.stop_render(stop_draw_thread=False) __RMAN_RENDER__.del_bl_engine() def selectObjectById(self): @@ -410,6 +410,9 @@ def is_regular_rendering(self): # return if we are doing a regular render and not interactive return (self.rman_running and not self.rman_interactive_running) + def is_ipr_to_it(self): + return (self.rman_interactive_running and self.rman_scene.ipr_render_into == 'it') + def do_draw_buckets(self): return get_pref('rman_viewport_draw_bucket', default=True) and self.rman_is_refining @@ -878,7 +881,7 @@ def start_interactive_render(self, context, depsgraph): rm = depsgraph.scene_eval.renderman self.it_port = start_cmd_server() render_into_org = '' - self.rman_render_into = rm.render_ipr_into + self.rman_render_into = self.rman_scene.ipr_render_into self.bl_viewport = context.space_data self.rman_callbacks.clear() diff --git a/rman_scene.py b/rman_scene.py index 47f6a367..f8276503 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -134,6 +134,8 @@ def __init__(self, rman_render=None): self.num_objects_in_viewlayer = 0 self.objects_in_viewlayer = list() + self.ipr_render_into = 'blender' + self.create_translators() @@ -243,7 +245,7 @@ def export_for_interactive_render(self, context, depsgraph, sg_scene): self.is_viewport_render = False self.rman_bake = False - if self.bl_scene.renderman.render_ipr_into == 'blender': + if self.ipr_render_into == 'blender': self.is_viewport_render = True self.do_motion_blur = False @@ -558,6 +560,8 @@ def check_visibility(self, instance): if not self.is_interactive: return True viewport = self.context.space_data + if viewport is None or viewport.type != 'VIEW_3D': + return True if instance.is_instance: ob_eval = instance.instance_object diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index 1bc86589..0a1bd15c 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -404,8 +404,12 @@ def draw(self, context): if not rm.is_rman_interactive_running: rman_rerender_controls = rfb_icons.get_icon("rman_ipr_on") - layout.operator('renderman.start_ipr', text="IPR", + op = layout.operator('renderman.start_ipr', text="IPR", icon_value=rman_rerender_controls.icon_id) + op.render_to_it = False + op = layout.operator('renderman.start_ipr', text="IPR to it", + icon_value=rman_rerender_controls.icon_id) + op.render_to_it = True rman_render_icon = rfb_icons.get_icon("rman_render") layout.operator("render.render", text="Render", icon_value=rman_render_icon.icon_id) diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index 32692793..27c20e1b 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -70,23 +70,9 @@ def draw(self, context): row = layout.row(align=True) rman_rerender_controls = rfb_icons.get_icon("rman_ipr_on") - row.operator('renderman.start_ipr', text="IPR", + op = row.operator('renderman.start_ipr', text="Start IPR to 'it'", icon_value=rman_rerender_controls.icon_id) - - row.prop(context.scene, "rm_ipr", text="", - icon=draw_utils.get_open_close_icon(context.scene.rm_ipr)) - - if context.scene.rm_ipr: - scene = context.scene - rd = scene.render - - box = layout.box() - box.use_property_split = True - box.use_property_decorate = False - row = box.row(align=True) - - # Display Driver - row.prop(rm, "render_ipr_into") + op.render_to_it = True row = layout.row(align=True) rman_batch = rfb_icons.get_icon("rman_batch") diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 859e46e4..89978108 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -798,7 +798,7 @@ def draw_rman_viewport_props(self, context): if context.engine == "PRMAN_RENDER": view = context.space_data rman_render = RmanRender.get_rman_render() - if view.shading.type == 'RENDERED': + if view.shading.type == 'RENDERED' or rman_render.is_ipr_to_it(): rman_rerender_controls = rfb_icons.get_icon("rman_ipr_cancel") row.operator('renderman.stop_ipr', text="", icon_value=rman_rerender_controls.icon_id) @@ -831,6 +831,7 @@ def draw_rman_viewport_props(self, context): # texture cache clear rman_icon = rfb_icons.get_icon('rman_lightning_grey') row.operator('rman_txmgr_list.clear_all_cache', text='', icon_value=rman_icon.icon_id) + elif rman_render.rman_running: rman_rerender_controls = rfb_icons.get_icon("rman_ipr_cancel") row.operator('renderman.stop_render', text="", @@ -844,8 +845,9 @@ def draw_rman_viewport_props(self, context): #rman_render.stop_render() rman_render.del_bl_engine() rman_rerender_controls = rfb_icons.get_icon("rman_ipr_on") - row.operator('renderman.start_ipr', text="", + op = row.operator('renderman.start_ipr', text="", icon_value=rman_rerender_controls.icon_id) + op.render_to_it = False row.popover(panel="PRMAN_PT_Viewport_Options", text="") @@ -880,6 +882,8 @@ def draw(self, context): if prefs.rman_viewport_draw_progress: col.prop(prefs, 'rman_viewport_progress_color') col.prop(prefs, 'draw_ipr_text') + if rm.current_platform != ("macOS"): + col.prop(rm, 'blender_ipr_optix_denoiser') if rm.current_platform != ("macOS") and rm.has_xpu_license: col = layout.column(align=True) From a298339a2fddbd6f4636e125372893b398b8dbb0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 3 Mar 2022 15:44:19 -0800 Subject: [PATCH 070/278] * Move the it handlers to their own file. * RmanRender will be responsible for adding/removing the handlers. --- rman_handlers/__init__.py | 41 +++++++--------------------- rman_handlers/rman_it_handlers.py | 45 +++++++++++++++++++++++++++++++ rman_render.py | 5 ++++ 3 files changed, 60 insertions(+), 31 deletions(-) create mode 100644 rman_handlers/rman_it_handlers.py diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index 7b5904c1..70794025 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -2,13 +2,13 @@ from ..rfb_utils import string_utils from ..rfb_utils import shadergraph_utils from ..rfb_utils import upgrade_utils -from ..rman_ui import rman_ui_light_handlers -from ..rman_render import RmanRender from bpy.app.handlers import persistent import bpy @persistent def rman_load_post(bl_scene): + from ..rman_ui import rman_ui_light_handlers + string_utils.update_blender_tokens_cb(bl_scene) rman_ui_light_handlers.clear_gl_tex_cache(bl_scene) texture_utils.txmanager_load_cb(bl_scene) @@ -25,26 +25,8 @@ def rman_save_post(bl_scene): texture_utils.txmanager_pre_save_cb(bl_scene) @persistent -def depsgraph_update_post(bl_scene, depsgraph): +def texture_despgraph_handler(bl_scene, depsgraph): texture_utils.depsgraph_handler(bl_scene, depsgraph) - rman_render = RmanRender.get_rman_render() - - # check updates if we are render ipring into it - if rman_render.is_ipr_to_it(): - context = bpy.context - rman_render.update_scene(context, depsgraph) - rman_render.update_view(context, depsgraph) - -@persistent -def frame_change_post(bl_scene): - rman_render = RmanRender.get_rman_render() - # check updates if we are render ipring into it - if rman_render.is_ipr_to_it(): - context = bpy.context - depsgraph = context.evaluated_depsgraph_get() - rman_render.update_scene(context, depsgraph) - rman_render.update_view(context, depsgraph) - def register(): @@ -60,12 +42,9 @@ def register(): if rman_save_post not in bpy.app.handlers.save_post: bpy.app.handlers.save_post.append(rman_save_post) - # depsgraph_update_post handler - if depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(depsgraph_update_post) - - if frame_change_post not in bpy.app.handlers.frame_change_post: - bpy.app.handlers.frame_change_post.append(frame_change_post) + # texture_depsgraph_update_post handler + if texture_despgraph_handler not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(texture_despgraph_handler) def unregister(): @@ -78,8 +57,8 @@ def unregister(): if rman_save_post in bpy.app.handlers.save_post: bpy.app.handlers.save_post.remove(rman_save_post) - if depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update_post) + if texture_despgraph_handler in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(texture_despgraph_handler) - if frame_change_post in bpy.app.handlers.frame_change_post: - bpy.app.handlers.frame_change_post.remove(frame_change_post) + from . import rman_it_handlers + rman_it_handlers.remove_ipr_to_it_handlers() diff --git a/rman_handlers/rman_it_handlers.py b/rman_handlers/rman_it_handlers.py new file mode 100644 index 00000000..1b0b28d1 --- /dev/null +++ b/rman_handlers/rman_it_handlers.py @@ -0,0 +1,45 @@ +from bpy.app.handlers import persistent +import bpy + +@persistent +def ipr_it_depsgraph_update_post(bl_scene, depsgraph): + from ..rman_render import RmanRender + rman_render = RmanRender.get_rman_render() + + # check updates if we are render ipring into it + if rman_render.is_ipr_to_it(): + context = bpy.context + rman_render.update_scene(context, depsgraph) + rman_render.update_view(context, depsgraph) + + +@persistent +def ipr_frame_change_post(bl_scene): + from ..rman_render import RmanRender + rman_render = RmanRender.get_rman_render() + # check updates if we are render ipring into it + if rman_render.is_ipr_to_it(): + context = bpy.context + depsgraph = context.evaluated_depsgraph_get() + rman_render.update_scene(context, depsgraph) + rman_render.update_view(context, depsgraph) + +def add_ipr_to_it_handlers(): + ''' + This adds handlers needed when we ipr to it + ''' + if ipr_it_depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(ipr_it_depsgraph_update_post) + + if ipr_frame_change_post not in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.append(ipr_frame_change_post) + +def remove_ipr_to_it_handlers(): + ''' + Remove handlers needed when we ipr to it + ''' + if ipr_it_depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(ipr_it_depsgraph_update_post) + + if ipr_frame_change_post in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.remove(ipr_frame_change_post) \ No newline at end of file diff --git a/rman_render.py b/rman_render.py index 0d8c9f3b..96d17422 100644 --- a/rman_render.py +++ b/rman_render.py @@ -33,6 +33,9 @@ # roz stats from .rman_stats import RfBStatsManager +# handlers +from .rman_handlers.rman_it_handlers import add_ipr_to_it_handlers, remove_ipr_to_it_handlers + __RMAN_RENDER__ = None __RMAN_IT_PORT__ = -1 __BLENDER_DSPY_PLUGIN__ = None @@ -899,6 +902,7 @@ def start_interactive_render(self, context, depsgraph): self._draw_viewport_buckets = True else: rman.Dspy.EnableDspyServer() + add_ipr_to_it_handlers() except: # force rendering to 'it' rfb_log().error('Could not register Blender display driver. Rendering to "it".') @@ -1100,6 +1104,7 @@ def stop_render(self, stop_draw_thread=True): for k,v in self.rman_callbacks.items(): ec.UnregisterCallback(k, v, self) self.rman_callbacks.clear() + remove_ipr_to_it_handlers() self.rman_is_live_rendering = False From e8de21e48b99be08d76091c2a99256bbe34046fe Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 8 Mar 2022 11:20:21 -0800 Subject: [PATCH 071/278] Modify the prototype_key function. Append either "-DATA" or "-OBJECT" if the name we're using is coming from data or if it's the object's name. This fixes an issue seen with the USD kitchen scene. A few of the empties had the same name as a couple of the mesh datablocks, which cause the RmanSceneSync to think the types didn't match and deleted the objects. --- rfb_utils/object_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 4cbcb9b0..8b13ec47 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -158,15 +158,15 @@ def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): if ob.is_instance: if ob.object.data: - return ob.object.data.name + return '%s-DATA' % ob.object.data.name else: - return ob.object.name + return '%s-OBJECT' % ob.object.name if ob.object.data: - return ob.object.data.name - return ob.object.original.name + return '%s-DATA' % ob.object.data.name + return '%s-OBJECT' % ob.object.original.name elif ob.data: - return ob.original.data.original.name - return ob.original.name + return '%s-DATA' % ob.original.data.original.name + return '%s-OBJECT' % ob.original.name def _detect_primitive_(ob): if ob is None: From 5e7c2663c304216c03d3a25d14a41fadb8cd47b0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 11 Mar 2022 11:12:47 -0800 Subject: [PATCH 072/278] First pass of creating a Qt version of the trace sets editor. --- .../rman_operators_editors_tracegroups.py | 284 ++++++++++++++++++ rman_ui/rfb_qt.py | 1 + 2 files changed, 285 insertions(+) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index b207a52b..9916f820 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -1,12 +1,284 @@ +from logging import root from bpy.props import (StringProperty, BoolProperty, EnumProperty) from ...rman_ui.rman_ui_base import CollectionPanel from ...rfb_logger import rfb_log from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config +from ...rfb_utils.prefs_utils import get_pref import bpy import re +import sys +rfb_qt = None +__TRACE_GROUPS_WINDOW__ = None +try: + from ...rman_ui import rfb_qt as rfb_qt +except: + pass + +if rfb_qt: + + from PySide2 import QtCore, QtWidgets, QtGui + class TraceGroupsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.trace_groups_qt_app_timed" + bl_label = "RenderMan Trace Sets Editor" + + def __init__(self): + super(TraceGroupsQtAppTimed, self).__init__() + + def execute(self, context): + self._window = TraceGroupsQtAppTimed() + return super(TraceGroupsQtAppTimed, self).execute(context) + + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + + class TraceGroupsQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(TraceGroupsQtWrapper, self).__init__() + + self.setWindowTitle('RenderMan Trace Groups') + self.resize(620, 475) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + + self.traceGroupObjects = QtWidgets.QTreeView(self) + self.traceGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.traceGroupObjects.setModel(self.treeModel) + + self.traceGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) + self.traceGroupObjects.setObjectName("traceGroupObjects") + self.traceGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + + self.traceGroups = QtWidgets.QListWidget(self) + self.traceGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.traceGroups.setObjectName("traceGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) + self.label.setText("Trace Groups") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Objects") + + self.refresh_btn = QtWidgets.QPushButton(self) + self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) + self.refresh_btn.setText("Refresh") + self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + QtCore.QMetaObject.connectSlotsByName(self) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.refresh_btn.clicked.connect(self.refresh_group_objects) + + self.traceGroups.itemChanged.connect(self.trace_group_changed) + self.traceGroups.itemSelectionChanged.connect(self.trace_groups_index_changed) + self.traceGroupObjects.selectionModel().selectionChanged.connect(self.trace_group_objects_selection) + + self.refresh_groups() + self.refresh_group_objects() + + self.traceGroupObjects.expandAll() + + def update(self): + idx = int(self.traceGroups.currentRow()) + self.addButton.setEnabled(True) + if self.traceGroups.count() < 1: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.enable_trace_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.removeButton.setEnabled(True) + self.enable_trace_group_objects(self.rootNode, enable=True) + + super(TraceGroupsQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.traceGroups.clear() + for grp in rm.object_groups: + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.traceGroups.addItem(item) + + if self.traceGroups.count() > 0: + self.traceGroups.setCurrentRow(rm.object_groups_index) + + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.object_groups.add() + grp.name = 'traceGroup_%d' % len(rm.object_groups) + rm.object_groups_index = len(rm.object_groups)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.object_groups_index + group = rm.object_groups[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.object_groups.remove(index) + rm.object_groups_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'OBJECT'}) + + self.refresh_groups() + + def trace_group_changed(self, item): + idx = int(self.traceGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.object_groups[idx] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + + def find_item(self, standard_item, ob): + if standard_item.text() == ob.name: + return standard_item + + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + if item.hasChildren(): + return self.find_item(item, ob) + + return None + + def enable_trace_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.enable_trace_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.traceGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + context = bpy.context + scene = context.scene + rm = scene.renderman + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: + item = self.find_item(root_item, child) + if not item: + item = StandardItem(txt=child.name) + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: + + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.traceGroupObjects.expandAll() + if idx != -1: + self.trace_groups_index_changed() + + def trace_groups_index_changed(self): + idx = int(self.traceGroups.currentRow()) + current_item = self.traceGroups.currentItem() + if current_item: + self.label_2.setText("Objects (%s)" % current_item.text()) + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.object_groups_index = idx + + group_index = rm.object_groups_index + object_groups = rm.object_groups + object_group = object_groups[group_index] + + selected_items = QtCore.QItemSelection() + for member in object_group.members: + ob = member.ob_pointer + if ob is None: + continue + item = self.find_item(self.rootNode, ob) + if item: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + + def trace_group_objects_selection(self, selected, deselected): + context = bpy.context + scene = context.scene + rm = scene.renderman + + group_index = rm.object_groups_index + object_groups = rm.object_groups + if group_index not in range(0, len(object_groups)): + return + object_group = object_groups[group_index] + + for i in selected.indexes(): + item = self.traceGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in object_group.members: + if ob == member.ob_pointer: + do_add = False + break + if do_add: + ob_in_group = object_group.members.add() + ob_in_group.name = ob.name + ob_in_group.ob_pointer = ob + ob.update_tag(refresh={'OBJECT'}) + + class RENDERMAN_UL_Object_Group_List(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -143,6 +415,15 @@ def __init__(self): def invoke(self, context, event): + if rfb_qt and get_pref('rman_ui_framework') == 'QT': + global __TRACE_GROUPS_WINDOW__ + if sys.platform == "darwin": + rfb_qt.run_with_timer(__TRACE_GROUPS_WINDOW__, TraceGroupsQtWrapper) + else: + wm.trace_groups_qt_app_timed() + + return {'RUNNING_MODAL'} + wm = context.window_manager width = rfb_config['editor_preferences']['tracesets_editor']['width'] self.event = event @@ -153,6 +434,9 @@ def invoke(self, context, event): RENDERMAN_UL_Object_Group_List ] +if rfb_qt: + classes.append(TraceGroupsQtAppTimed) + def register(): for cls in classes: bpy.utils.register_class(cls) diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index e842d96e..b7aebe87 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -98,6 +98,7 @@ def process_qt_events(app, window): if window and not window.isVisible(): return None app.processEvents() + window.update() return 0.01 # Run again after 0.001 seconds def run_with_timer(window, cls): From 5c447a708e0758506f9fe05f5eeef896f3e9c5ec Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Mar 2022 10:32:13 -0700 Subject: [PATCH 073/278] Default to 1 for rman_roz_stats_print_level --- rman_stats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index a33cfc39..32c3bdd7 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -209,7 +209,7 @@ def update_session_config(self): self.mgr.serverId = self.web_socket_server_id # update what stats to draw - print_level = int(prefs_utils.get_pref('rman_roz_stats_print_level')) + print_level = int(prefs_utils.get_pref('rman_roz_stats_print_level', default='1')) if print_level == 1: self.stats_to_draw = __BASIC_STATS__ elif print_level == 2: From 03074880bd14d6722d5d581ad3d7694d154bae42 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 17 Mar 2022 10:33:49 -0700 Subject: [PATCH 074/278] Revert previous change to rman_roz_stats_print_level --- rman_stats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index 32c3bdd7..a33cfc39 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -209,7 +209,7 @@ def update_session_config(self): self.mgr.serverId = self.web_socket_server_id # update what stats to draw - print_level = int(prefs_utils.get_pref('rman_roz_stats_print_level', default='1')) + print_level = int(prefs_utils.get_pref('rman_roz_stats_print_level')) if print_level == 1: self.stats_to_draw = __BASIC_STATS__ elif print_level == 2: From ed59af3710504ac8a639db91e8043065d36ab5e9 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 24 Mar 2022 14:52:41 -0700 Subject: [PATCH 075/278] Remove the code that drew the "RenderMan Interactive Mode Running" message in the viewport. --- __init__.py | 14 -------------- preferences.py | 7 ------- 2 files changed, 21 deletions(-) diff --git a/__init__.py b/__init__.py index 24626afe..4dd7d488 100644 --- a/__init__.py +++ b/__init__.py @@ -248,20 +248,6 @@ def _draw_pixels(self, context, depsgraph): blf.disable(0, blf.SHADOW) return - # Draw text area that RenderMan is running. - if get_pref('draw_ipr_text', False) and not self.rman_render.rman_is_viewport_rendering: - - pos_x = w / 2 - 100 - pos_y = 20 - blf.enable(0, blf.SHADOW) - blf.shadow_offset(0, 1, -1) - blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) - blf.size(0, 32, 36) - blf.position(0, pos_x, pos_y, 0) - blf.color(0, 1.0, 0.0, 0.0, 1.0) - blf.draw(0, "%s" % ('RenderMan Interactive Mode Running')) - blf.disable(0, blf.SHADOW) - if not self.rman_render.rman_is_viewport_rendering: return diff --git a/preferences.py b/preferences.py index 31163b13..5df0f6f5 100644 --- a/preferences.py +++ b/preferences.py @@ -41,7 +41,6 @@ 'rman_xpu_gpu_selection': -1, 'rman_xpu_device': 'CPU', 'rman_xpu_cpu_devices': [], - 'draw_ipr_text': True, 'draw_panel_icon': True, 'path_fallback_textures_path': os.path.join('', 'textures'), 'path_fallback_textures_path_always': False, @@ -225,11 +224,6 @@ def reload_rmantree(self, context): update=lambda s,c: fix_path(s, 'path_rmantree') ) - draw_ipr_text: BoolProperty( - name="Draw IPR Text", - description="Draw notice on View3D when IPR is active", - default=True) - draw_panel_icon: BoolProperty( name="Draw Panel Icon", description="Draw an icon on RenderMan Panels", @@ -638,7 +632,6 @@ def draw(self, context): col.prop(self, 'rman_viewport_draw_progress') if self.rman_viewport_draw_progress: col.prop(self, 'rman_viewport_progress_color') - col.prop(self, 'draw_ipr_text') col.prop(self, 'draw_panel_icon') col.prop(self, 'rman_ui_framework') From b509004a5df05a0013db304d7e478d5f6fdfd136 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Mar 2022 10:42:08 -0700 Subject: [PATCH 076/278] Some improvements to the hair translator * Add a BlHair class to hold the data we're extracting from Blender * Don't transform each point to object space and instead just SetTransform on the Curve scene graph node * Like emitters, for deformation blur do velocity based motion blur. --- rman_scene.py | 2 + rman_translators/rman_hair_translator.py | 177 ++++++++++++----------- 2 files changed, 98 insertions(+), 81 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index f8276503..3026db63 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -842,6 +842,7 @@ def export_instances_motion(self, selected_objects=False): continue # check particles for motion + ''' for psys in ob.particle_systems: ob_psys = self.rman_particles.get(proto_key, None) if not ob_psys: @@ -857,6 +858,7 @@ def export_instances_motion(self, selected_objects=False): idx = i break psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) + ''' # object is not moving and not part of a particle system if ob.name_full not in self.moving_objects and not psys: diff --git a/rman_translators/rman_hair_translator.py b/rman_translators/rman_hair_translator.py index 242e6fb8..68aaf032 100644 --- a/rman_translators/rman_hair_translator.py +++ b/rman_translators/rman_hair_translator.py @@ -1,5 +1,5 @@ from .rman_translator import RmanTranslator -from ..rfb_utils import object_utils +from ..rfb_utils import transform_utils from ..rfb_utils import scenegraph_utils from ..rfb_logger import rfb_log from ..rman_sg_nodes.rman_sg_hair import RmanSgHair @@ -7,8 +7,17 @@ import math import bpy import numpy as np - +class BlHair: + + def __init__(self): + self.points = [] + self.next_points = [] + self.vertsArray = [] + self.scalpST = [] + self.mcols = [] + self.nverts = 0 + self.hair_width = None class RmanHairTranslator(RmanTranslator): def __init__(self, rman_scene): @@ -30,16 +39,22 @@ def clear_children(self, ob, psys, rman_sg_hair): rman_sg_hair.sg_curves_list.clear() def export_deform_sample(self, rman_sg_hair, ob, psys, time_sample): + return + + ''' + # Keep this code below, in case we want to give users the option + # to do non-velocity based motion blur curves = self._get_strands_(ob, psys) - for i, (vertsArray, points, widths, scalpST, mcols) in enumerate(curves): + for i, bl_curve in enumerate(curves): curves_sg = rman_sg_hair.sg_curves_list[i] if not curves_sg: continue primvar = curves_sg.GetPrimVars() - primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, points, "vertex", time_sample) + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.points, "vertex", time_sample) curves_sg.SetPrimVars(primvar) + ''' def update(self, ob, psys, rman_sg_hair): if rman_sg_hair.sg_node: @@ -50,32 +65,38 @@ def update(self, ob, psys, rman_sg_hair): if not curves: return - for i, (vertsArray, points, widths, scalpST, mcols) in enumerate(curves): + ob_inv_mtx = transform_utils.convert_matrix(ob.matrix_world.inverted_safe()) + for i, bl_curve in enumerate(curves): curves_sg = self.rman_scene.sg_scene.CreateCurves("%s-%d" % (rman_sg_hair.db_name, i)) - curves_sg.Define(self.rman_scene.rman.Tokens.Rix.k_cubic, "nonperiodic", "catmull-rom", len(vertsArray), len(points)) - primvar = curves_sg.GetPrimVars() + curves_sg.SetTransform(ob_inv_mtx) # puts points in object space + curves_sg.Define(self.rman_scene.rman.Tokens.Rix.k_cubic, "nonperiodic", "catmull-rom", len(bl_curve.vertsArray), len(bl_curve.points)) + primvar = curves_sg.GetPrimVars() + if rman_sg_hair.motion_steps: + super().set_primvar_times(rman_sg_hair.motion_steps, primvar) + + if self.rman_scene.do_motion_blur: + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.points, "vertex", 0) + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.next_points, "vertex", 1) + else: + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.points, "vertex") - primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, points, "vertex") - primvar.SetIntegerDetail(self.rman_scene.rman.Tokens.Rix.k_Ri_nvertices, vertsArray, "uniform") + primvar.SetIntegerDetail(self.rman_scene.rman.Tokens.Rix.k_Ri_nvertices, bl_curve.vertsArray, "uniform") index_nm = psys.settings.renderman.hair_index_name if index_nm == '': index_nm = 'index' - primvar.SetIntegerDetail(index_nm, range(len(vertsArray)), "uniform") + primvar.SetIntegerDetail(index_nm, range(len(bl_curve.vertsArray)), "uniform") width_detail = "constant" - if isinstance(widths, list): + if isinstance(bl_curve.hair_width, list): width_detail = "vertex" - primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, widths, width_detail) + primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, bl_curve.hair_width, width_detail) - if len(scalpST): - primvar.SetFloatArrayDetail("scalpST", scalpST, 2, "uniform") + if len(bl_curve.scalpST): + primvar.SetFloatArrayDetail("scalpST", bl_curve.scalpST, 2, "uniform") - if len(mcols): - primvar.SetColorDetail("Cs", mcols, "uniform") + if len(bl_curve.mcols): + primvar.SetColorDetail("Cs", bl_curve.mcols, "uniform") - if rman_sg_hair.motion_steps: - super().set_primvar_times(rman_sg_hair.motion_steps, primvar) - curves_sg.SetPrimVars(primvar) rman_sg_hair.sg_node.AddChild(curves_sg) rman_sg_hair.sg_curves_list.append(curves_sg) @@ -113,19 +134,13 @@ def _get_strands_(self, ob, psys): tip_width = psys.settings.tip_radius * psys.settings.radius_scale base_width = psys.settings.root_radius * psys.settings.radius_scale - - conwidth = (tip_width == base_width) + conwidth = (tip_width == base_width) if self.rman_scene.is_interactive: steps = (2 ** psys.settings.display_step)+1 else: steps = (2 ** psys.settings.render_step)+1 - if conwidth: - hair_width = base_width - else: - hair_width = [] - num_parents = len(psys.particles) num_children = len(psys.child_particles) rm = psys.settings.renderman @@ -150,19 +165,21 @@ def _get_strands_(self, ob, psys): break curve_sets = [] - points = [] - vertsArray = [] - scalpST = [] - mcols = [] - nverts = 0 + bl_curve = BlHair() + if conwidth: + bl_curve.hair_width = base_width + else: + bl_curve.hair_width = [] - ob_inv_mtx = ob.matrix_world.inverted_safe() start_idx = 0 if psys.settings.child_type != 'NONE' and num_children > 0: start_idx = num_parents for pindex in range(start_idx, total_hair_count): + particle = psys.particles[ + (pindex - num_parents) % num_parents] strand_points = [] + next_strand_points = [] # walk through each strand for step in range(0, steps): pt = psys.co_hair(ob, particle_no=pindex, step=step) @@ -171,63 +188,61 @@ def _get_strands_(self, ob, psys): # this strand ends prematurely break - # put points in object space - pt = ob_inv_mtx @ pt - strand_points.append(pt) - if len(strand_points) > 1: - # double the first and last - strand_points = strand_points[:1] + \ - strand_points + strand_points[-1:] - vertsInStrand = len(strand_points) + if len(strand_points) < 2: + # not enought points + continue - # catmull-rom requires at least 4 vertices - if vertsInStrand < 4: - continue + # double the first and last + strand_points = strand_points[:1] + \ + strand_points + strand_points[-1:] + + vertsInStrand = len(strand_points) - # for varying width make the width array - if not conwidth: - decr = (base_width - tip_width) / (vertsInStrand - 2) - hair_width.extend([base_width] + [(base_width - decr * i) - for i in range(vertsInStrand - 2)] + - [tip_width]) - - # add the last point again - points.extend(strand_points) - vertsArray.append(vertsInStrand) - nverts += vertsInStrand - - # get the scalp ST - if export_st: - particle = psys.particles[ - (pindex - num_parents) % num_parents] - st = psys.uv_on_emitter(psys_modifier, particle=particle, particle_no=pindex, uv_no=uv_set) - scalpST.append([st[0], st[1]]) - - if export_mcol: - particle = psys.particles[ - (pindex - num_parents) % num_parents] - mcol = psys.mcol_on_emitter(psys_modifier, particle=particle, particle_no=pindex, vcol_no=mcol_set) - mcols.append([mcol[0], mcol[1], mcol[2]]) - - # if we get more than 100000 vertices, export ri.Curve and reset. This + # catmull-rom requires at least 4 vertices + if vertsInStrand < 4: + continue + + if self.rman_scene.do_motion_blur: + # calculate the points for the next frame using velocity + vel = Vector(particle.velocity / particle.lifetime ) + next_strand_points = [p + vel for p in strand_points] + + # for varying width make the width array + if not conwidth: + decr = (base_width - tip_width) / (vertsInStrand - 2) + bl_curve.hair_width.extend([base_width] + [(base_width - decr * i) + for i in range(vertsInStrand - 2)] + + [tip_width]) + + bl_curve.points.extend(strand_points) + bl_curve.next_points.extend(next_strand_points) + bl_curve.vertsArray.append(vertsInStrand) + bl_curve.nverts += vertsInStrand + + # get the scalp ST + if export_st: + st = psys.uv_on_emitter(psys_modifier, particle=particle, particle_no=pindex, uv_no=uv_set) + bl_curve.scalpST.append([st[0], st[1]]) + + if export_mcol: + mcol = psys.mcol_on_emitter(psys_modifier, particle=particle, particle_no=pindex, vcol_no=mcol_set) + bl_curve.mcols.append([mcol[0], mcol[1], mcol[2]]) + + # if we get more than 100000 vertices, start a new BlHair. This # is to avoid a maxint on the array length - if nverts > 100000: - curve_sets.append( - (vertsArray, points, hair_width, scalpST, mcols)) + if bl_curve.nverts > 100000: + curve_sets.append(bl_curve) + bl_curve = BlHair() - nverts = 0 - points = [] - vertsArray = [] if not conwidth: - hair_width = [] - scalpST = [] - mcols = [] + bl_curve.hair_width = [] + else: + bl_curve.hair_width = base_width - if nverts > 0: - curve_sets.append((vertsArray, points, - hair_width, scalpST, mcols)) + if bl_curve.nverts > 0: + curve_sets.append(bl_curve) return curve_sets \ No newline at end of file From 9ceeef16832eeb8927884124bb73cc482d029472 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Mar 2022 12:31:50 -0700 Subject: [PATCH 077/278] Don't check for a render border if the viewport is not rendering. We'll need to rethink this code when we can support multiple viewport renders. --- rman_ui/rman_ui_viewport.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 89978108..a6915882 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -6,6 +6,7 @@ from ..rfb_utils.prefs_utils import get_pref, get_addon_prefs from ..rfb_utils import display_utils from ..rfb_utils import camera_utils +from ..rfb_logger import rfb_log from bpy.types import Menu import bpy @@ -251,7 +252,9 @@ def get_crop_window(self, width, height): return [remap_start_x, remap_end_x, remap_start_y, remap_end_y] def check_render_border(self): - space = bpy.context.space_data + space = bpy.context.space_data + if space.shading.type != 'RENDERED': + return region_data = bpy.context.region_data region = bpy.context.region scene = bpy.context.scene From b348b5ac2b66411e09514658c2bdcab9d48e6a87 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Mar 2022 12:42:34 -0700 Subject: [PATCH 078/278] Missing a parameter to get_out_param_str call, in translate_cycles_math_node function --- rman_translators/rman_material_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_translators/rman_material_translator.py b/rman_translators/rman_material_translator.py index 6c7be2b5..3e19adcf 100644 --- a/rman_translators/rman_material_translator.py +++ b/rman_translators/rman_material_translator.py @@ -296,7 +296,7 @@ def translate_cycles_math_node(self, mat, rman_sg_material, node, mat_name, grou param_type = 'float' if input.is_linked: link = input.links[0] - val = property_utils.get_output_param_str( + val = property_utils.get_output_param_str(rman_sg_material, link.from_node, mat_name, link.from_socket, input) property_utils.set_rix_param(params, param_type, param_name, val, is_reference=True) From 8781379b585ec200596678485413600773284af9 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 25 Mar 2022 13:16:10 -0700 Subject: [PATCH 079/278] Move the function that checks if we should consider a curve to be a mesh to object_utils. We need to re-use this function in _detect_primitive_ --- rfb_utils/object_utils.py | 20 ++++++++++++++++++++ rman_translators/rman_curve_translator.py | 17 +---------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 8b13ec47..255d2143 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -168,6 +168,24 @@ def prototype_key(ob): return '%s-DATA' % ob.original.data.original.name return '%s-OBJECT' % ob.original.name +def curve_is_mesh(ob): + ''' + Check if we need to consider this curve a mesh + ''' + is_mesh = False + if len(ob.modifiers) > 0: + is_mesh = True + elif len(ob.data.splines) < 1: + is_mesh = True + elif ob.data.dimensions == '2D' and ob.data.fill_mode != 'NONE': + is_mesh = True + else: + l = ob.data.extrude + ob.data.bevel_depth + if l > 0: + is_mesh = True + + return is_mesh + def _detect_primitive_(ob): if ob is None: return '' @@ -192,6 +210,8 @@ def _detect_primitive_(ob): elif ob.type == 'FONT': return 'MESH' elif ob.type in ['CURVE']: + if curve_is_mesh(ob): + return 'MESH' return 'CURVE' elif ob.type == 'SURFACE': if get_pref('rman_render_nurbs_as_mesh', True): diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index cb570d7e..3544922a 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -118,21 +118,6 @@ def export(self, ob, db_name): return rman_sg_curve - def _is_mesh(self, ob): - is_mesh = False - if len(ob.modifiers) > 0: - is_mesh = True - elif len(ob.data.splines) < 1: - is_mesh = True - elif ob.data.dimensions == '2D' and ob.data.fill_mode != 'NONE': - is_mesh = True - else: - l = ob.data.extrude + ob.data.bevel_depth - if l > 0: - is_mesh = True - - return is_mesh - def export_deform_sample(self, rman_sg_curve, ob, time_sample): if rman_sg_curve.is_mesh: super().export_deform_sample(rman_sg_curve, ob, time_sample, sg_node=rman_sg_curve.sg_mesh_node) @@ -146,7 +131,7 @@ def update(self, ob, rman_sg_curve): rman_sg_curve.sg_node.RemoveChild(c) self.rman_scene.sg_scene.DeleteDagNode(c) - rman_sg_curve.is_mesh = self._is_mesh(ob) + rman_sg_curve.is_mesh = object_utils.curve_is_mesh(ob) if rman_sg_curve.is_mesh: rman_sg_curve.sg_mesh_node = self.rman_scene.sg_scene.CreateMesh('%s-MESH' % rman_sg_curve.db_name) From 1544a5d8af50128c462f81bfb5405b319f01c9d0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 31 Mar 2022 15:01:21 -0700 Subject: [PATCH 080/278] Add a checkbox for exporting the default tangent and bitangent primvars for meshes (default is off). Having this on can easily double the export time, so we don't necessarily want to do this for every mesh. --- rman_config/config/rman_properties_curve.json | 16 ++++++++++++++++ rman_config/config/rman_properties_mesh.json | 16 ++++++++++++++++ rman_translators/rman_mesh_translator.py | 6 +++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/rman_config/config/rman_properties_curve.json b/rman_config/config/rman_properties_curve.json index 4cd01905..a739a3c8 100644 --- a/rman_config/config/rman_properties_curve.json +++ b/rman_config/config/rman_properties_curve.json @@ -97,6 +97,22 @@ "help": "Export the active UV set as the default 'st' primitive variable", "page": "" }, + { + "panel": "MESH_PT_renderman_prim_vars", + "name": "export_default_tangents", + "label": "Export Default Tangents", + "type": "int", + "editable": true, + "default": 0, + "widget": "checkbox", + "help": "Export the tangent and bitangent vectors for the active UV set. They will be exported as 'Tn' and 'Bn' primitive variables, respectively", + "page": "", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "export_default_uv", + "conditionalVisValue": "0" + } + }, { "panel": "CURVE_PT_renderman_prim_vars", "name": "export_default_vcol", diff --git a/rman_config/config/rman_properties_mesh.json b/rman_config/config/rman_properties_mesh.json index 2ef4321f..736a9428 100644 --- a/rman_config/config/rman_properties_mesh.json +++ b/rman_config/config/rman_properties_mesh.json @@ -97,6 +97,22 @@ "help": "Export the active UV set as the default 'st' primitive variable", "page": "" }, + { + "panel": "MESH_PT_renderman_prim_vars", + "name": "export_default_tangents", + "label": "Export Default Tangents", + "type": "int", + "editable": true, + "default": 0, + "widget": "checkbox", + "help": "Export the tangent and bitangent vectors for the active UV set. They will be exported as 'Tn' and 'Bn' primitive variables, respectively", + "page": "", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "export_default_uv", + "conditionalVisValue": "0" + } + }, { "panel": "MESH_PT_renderman_prim_vars", "name": "export_default_vcol", diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index ff647a3c..8a127d27 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -46,8 +46,7 @@ def _get_mesh_uv_(mesh, name=""): uv_count = len(uv_loop_layer.data) fastuvs = np.zeros(uv_count * 2) - uv_loop_layer.data.foreach_get("uv", fastuvs) - fastuvs = fastuvs.reshape(uv_count, 2) + uv_loop_layer.data.foreach_get("uv", fastuvs) uvs = fastuvs.tolist() return uvs @@ -202,7 +201,8 @@ def _get_primvars_(ob, rman_sg_mesh, geo, rixparams): if uvs and len(uvs) > 0: detail = "facevarying" if facevarying_detail == len(uvs) else "vertex" rixparams.SetFloatArrayDetail("st", uvs, 2, detail) - export_tangents(ob, geo, rixparams) + if rm.export_default_tangents: + export_tangents(ob, geo, rixparams) if rm.export_default_vcol: vcols = _get_mesh_vcol_(geo) From 17c50298f593b86614d7b140b76b8c141ce417cc Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 31 Mar 2022 16:27:07 -0700 Subject: [PATCH 081/278] Minor cleanup in hair translator code. Added a comment. --- rman_translators/rman_hair_translator.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rman_translators/rman_hair_translator.py b/rman_translators/rman_hair_translator.py index 68aaf032..4c835562 100644 --- a/rman_translators/rman_hair_translator.py +++ b/rman_translators/rman_hair_translator.py @@ -10,14 +10,17 @@ class BlHair: - def __init__(self): + def __init__(self, constant_width=None): self.points = [] self.next_points = [] self.vertsArray = [] self.scalpST = [] self.mcols = [] self.nverts = 0 - self.hair_width = None + if constant_width is not None: + self.hair_width = constant_width + else: + self.hair_width = None class RmanHairTranslator(RmanTranslator): def __init__(self, rman_scene): @@ -224,11 +227,12 @@ def _get_strands_(self, ob, psys): # get the scalp ST if export_st: st = psys.uv_on_emitter(psys_modifier, particle=particle, particle_no=pindex, uv_no=uv_set) - bl_curve.scalpST.append([st[0], st[1]]) + bl_curve.scalpST.append(st) + # get mcol if export_mcol: mcol = psys.mcol_on_emitter(psys_modifier, particle=particle, particle_no=pindex, vcol_no=mcol_set) - bl_curve.mcols.append([mcol[0], mcol[1], mcol[2]]) + bl_curve.mcols.append(mcol) # if we get more than 100000 vertices, start a new BlHair. This # is to avoid a maxint on the array length From 7a460cc1bab5058e092fba26a16a6732d34829cc Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 5 Apr 2022 09:14:22 -0700 Subject: [PATCH 082/278] Switch cookie and gobo light filters to use the same draw handler as barn. This should match what RfM does. --- rman_ui/rman_ui_light_handlers/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index 06213558..019c6a8b 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -1511,13 +1511,13 @@ def draw(): draw_dome_light(ob) elif light_shader_name == 'PxrCylinderLight': draw_cylinder_light(ob) - elif light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter', 'PxrRectLight']: + elif light_shader_name in ['PxrRectLight']: draw_rect_light(ob) elif light_shader_name in ['PxrRodLightFilter', 'PxrBlockerLightFilter']: draw_rod_light_filter(ob) elif light_shader_name == 'PxrRampLightFilter': draw_ramp_light_filter(ob) - elif light_shader_name == 'PxrBarnLightFilter': + elif light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter', 'PxrBarnLightFilter']: # get all lights that the barn is attached to draw_barn_light_filter(ob) else: From 09bbcd7227756aabda1af0e597c2bd5549a57711 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 7 Apr 2022 09:29:10 -0700 Subject: [PATCH 083/278] Fix typo in curve translator. --- rman_translators/rman_curve_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index 3544922a..d54e86e8 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -108,7 +108,7 @@ def __init__(self, rman_scene): def export(self, ob, db_name): sg_node = self.rman_scene.sg_scene.CreateGroup(db_name) - is_mesh = self._is_mesh(ob) + is_mesh = object_utils.curve_is_mesh(ob) rman_sg_curve = RmanSgCurve(self.rman_scene, sg_node, db_name) rman_sg_curve.is_mesh = is_mesh From 2b197c2c5af9e78516a24136880b75d9719ab4df Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 7 Apr 2022 09:41:35 -0700 Subject: [PATCH 084/278] Fix issue with mesh UVs. We are now grabbing the uv's as a flat list, so we need to compare with double the length when trying to determine if we are varyinf or facevarying. --- rman_translators/rman_mesh_translator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 8a127d27..c3d1d4c5 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -199,7 +199,7 @@ def _get_primvars_(ob, rman_sg_mesh, geo, rixparams): if rm.export_default_uv: uvs = _get_mesh_uv_(geo) if uvs and len(uvs) > 0: - detail = "facevarying" if facevarying_detail == len(uvs) else "vertex" + detail = "facevarying" if (facevarying_detail*2) == len(uvs) else "vertex" rixparams.SetFloatArrayDetail("st", uvs, 2, detail) if rm.export_default_tangents: export_tangents(ob, geo, rixparams) @@ -226,7 +226,7 @@ def _get_primvars_(ob, rman_sg_mesh, geo, rixparams): elif p.data_source == 'UV_TEXTURE': uvs = _get_mesh_uv_(geo, p.data_name) if uvs and len(uvs) > 0: - detail = "facevarying" if facevarying_detail == len(uvs) else "vertex" + detail = "facevarying" if (facevarying_detail*2) == len(uvs) else "vertex" rixparams.SetFloatArrayDetail(p.name, uvs, 2, detail) if p.export_tangents: export_tangents(ob, geo, rixparams, uvmap=p.data_name, name=p.name) From d11c1a6c61becaa0bb06d2c413620bcd9bbdebe3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 8 Apr 2022 11:40:40 -0700 Subject: [PATCH 085/278] Shave a couple of seconds of export time for the Eisko Louise scene. Switch to numpy for retrieving the bspline curve points and widths. --- rman_translators/rman_curve_translator.py | 41 ++++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index d54e86e8..e06d15b4 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -6,6 +6,7 @@ import bpy import math +import numpy as np def get_bspline_curve(curve): P = [] @@ -17,15 +18,17 @@ def get_bspline_curve(curve): for i, spline in enumerate(curve.splines): - width = [] - for bp in spline.points: - P.append([bp.co[0], bp.co[1], bp.co[2]]) - w = bp.radius * 0.01 - if w < 0.01: - w = 0.01 - width.extend( 3 * [w]) + npoints = len(spline.points) + pts = np.zeros(npoints*4, dtype=np.float32) + width = np.zeros(npoints, dtype=np.float32) - widths.append(width) + spline.points.foreach_get('co', pts) + spline.points.foreach_get('radius', width) + pts = np.reshape(pts, (npoints, 4)) + width = np.where(width >= 1.0, width*0.01, 0.01) + + P.extend(pts[0:, 0:3].tolist()) + widths.append(width.tolist()) index.append(i) nvertices.append(len(spline.points)) name = spline.id_data.name @@ -42,13 +45,16 @@ def get_curve(curve): for i, spline in enumerate(curve.splines): - width = [] - for bp in spline.points: - P.append([bp.co[0], bp.co[1], bp.co[2]]) - w = bp.radius * 0.01 - if w < 0.01: - w = 0.01 - width.extend( 3 * [w]) + npoints = len(spline.points) + pts = np.zeros(npoints*4, dtype=np.float32) + width = np.zeros(npoints, dtype=np.float32) + + spline.points.foreach_get('co', pts) + spline.points.foreach_get('radius', width) + pts = np.reshape(pts, (npoints, 4)) + width = np.where(width >= 1.0, width*0.01, 0.01) + + P.extend(pts[0:, 0:3].tolist()) widths.append(width) index.append(i) @@ -68,7 +74,10 @@ def get_bezier_curve(curve): P.append(bp.handle_left) P.append(bp.co) P.append(bp.handle_right) - width.extend( 3 * [bp.radius * 0.01]) + w = bp.radius * 0.01 + if w < 0.01: + w = 0.01 + width.extend( 3 * [w]) if spline.use_cyclic_u: period = 'periodic' From a9f25c1a0691f60e6fb3553c31f3e02b8e7dd878 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 14 Apr 2022 10:29:28 -0700 Subject: [PATCH 086/278] Add some missing volume primvars: * volume:temporalmethod * volume:fps * volume:shutteroffset * volume:velocityshuttercorrection Some other changes as well: * merge the setting of general primvars into its own function in property_utils * we now check the conditionalVisOps first to see if the primar actually should be applied. --- rfb_utils/property_utils.py | 44 ++++++- .../config/rman_properties_object.json | 119 ++++++++++++++++-- .../config/rman_properties_volume.json | 4 +- .../rman_properties_object/__init__.py | 4 +- rman_translators/rman_mesh_translator.py | 33 +---- rman_translators/rman_openvdb_translator.py | 16 +-- rman_translators/rman_translator.py | 40 ++---- rman_ui/rman_ui_object_panels.py | 3 +- 8 files changed, 175 insertions(+), 88 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index c7ac8d5d..e6a34bcc 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -1,3 +1,4 @@ +from operator import methodcaller from . import texture_utils from . import string_utils from . import shadergraph_utils @@ -132,7 +133,7 @@ def get_property_default(node, prop_name): return dflt -def set_rix_param(params, param_type, param_name, val, is_reference=False, is_array=False, array_len=-1, node=None): +def set_rix_param(params, param_type, param_name, val, is_reference=False, is_array=False, array_len=-1, node=None, prop_name=''): """Sets a single parameter in an RtParamList Arguments: @@ -145,6 +146,7 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar array_len (int) - length of array node (AnyType) - the Blender object that this param originally came from. This is necessary so we can grab and compare val with the default value (see get_property_default) + prop_name (str) - name of the property that we look for the default value """ @@ -176,7 +178,10 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar else: # check if we need to emit this parameter. if node != None and not prefs_utils.get_pref('rman_emit_default_params', False): - dflt = get_property_default(node, param_name) + if prop_name != '': + dflt = get_property_default(node, prop_name) + else: + dflt = get_property_default(node, param_name) # FIXME/TODO: currently, the python version of RtParamList # doesn't allow us to retrieve existing values. For now, only do the @@ -242,7 +247,40 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar elif param_type == "vector": params.SetVector(param_name, val) elif param_type == "normal": - params.SetNormal(param_name, val) + params.SetNormal(param_name, val) + +def set_primvar_bl_props(primvars, rm, inherit_node=None): + # set any properties marked primvar in the config file + for prop_name, meta in rm.prop_meta.items(): + if 'primvar' not in meta: + continue + + conditionalVisOps = meta.get('conditionalVisOps', None) + if conditionalVisOps: + # check conditionalVisOps to see if this primvar applies + # to this object + expr = conditionalVisOps.get('expr', None) + node = rm + if expr and not eval(expr): + continue + + val = getattr(rm, prop_name) + if not val: + continue + + if 'inheritable' in meta: + if float(val) == meta['inherit_true_value']: + if inherit_node and hasattr(inherit_node, prop_name): + val = getattr(inherit_node, prop_name) + + ri_name = meta['primvar'] + is_array = False + array_len = -1 + if 'arraySize' in meta: + is_array = True + array_len = meta['arraySize'] + param_type = meta['renderman_type'] + set_rix_param(primvars, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) def build_output_param_str(rman_sg_node, mat_name, from_node, from_socket, convert_socket=False, param_type=''): nodes_to_blnodeinfo = getattr(rman_sg_node, 'nodes_to_blnodeinfo', dict()) diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index e16e99cf..5f04f01d 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -679,14 +679,21 @@ "page": "Dicing", "help": "Micropolygon distance in raster space for 'instanceprojection' dicing. Values are expressed in pixel size.", "conditionalVisOps": { - "conditionalVisOp": "notEqualTo", - "conditionalVisPath": "bl_object_type", - "conditionalVisValue": "OPENVDB" - } + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "notEqualTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "and", + "conditionalVis1Op": "notEqualTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + } }, { "panel": "OBJECT_PT_renderman_object_geometry_attributes", "name": "rman_micropolygonlength_volume", + "primvar": "dice:micropolygonlength", "label": "MicroPolygon Length", "type": "float", "default": 10.0, @@ -696,10 +703,16 @@ "page": "Dicing", "help": "Micropolygon distance in raster space for 'instanceprojection' dicing. Values are expressed in pixel size.", "conditionalVisOps": { - "conditionalVisOp": "equalTo", - "conditionalVisPath": "bl_object_type", - "conditionalVisValue": "OPENVDB" - } + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + } }, { "panel": "OBJECT_PT_renderman_object_geometry_attributes", @@ -801,6 +814,96 @@ "conditionalVisValue": 1 } }, + { + "panel": "OBJECT_PT_renderman_object_geometry_volume", + "page": "", + "name": "volume_temporalmethod", + "label": "Temporal Method", + "primvar": "volume:temporalmethod", + "type": "int", + "editable": true, + "default": 0, + "widget": "mapper", + "options": "Eulerian:0|Reves:1", + "conditionalVisOps": { + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + }, + "help": "Method of generating temporal data for volume rendering." + }, + { + "panel": "OBJECT_PT_renderman_object_geometry_volume", + "page": "", + "name": "volume_fps", + "label": "Velocity frames per second", + "primvar": "volume:fps", + "type": "float", + "editable": true, + "default": 1, + "conditionalVisOps": { + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + }, + "help": "The frames per second for volumetric velocity data. The velocity data will be divided by this quantity to derive the velocity for the frame." + }, + { + "panel": "OBJECT_PT_renderman_object_geometry_volume", + "page": "", + "name": "volume_shutteroffset", + "label": "Shutter Offset", + "primvar": "volume:shutteroffset", + "type": "float", + "editable": true, + "default": 1, + "conditionalVisOps": { + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + }, + "help": "The shutter offset used to interpret volumetric velocity. A value of 1 will use the current position of the object and the position of the object on the next frame as the time interval to use for motion blur. A value of -1 will use the position of the object on the previous frame and the current position of the object as the time. A value of 0 will generate an interval which begins halfway through the previous frame and ends halfway into the next frame." + }, + { + "panel": "OBJECT_PT_renderman_object_geometry_volume", + "page": "", + "name": "volume_velocityshuttercorrection", + "label": "Velocity Shutter Correction", + "primvar": "volume:velocityshuttercorrection", + "type": "int", + "editable": true, + "default": 0, + "conditionalVisOps": { + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + }, + "help": "The shutter offset used to interpret volumetric velocity. A value of 1 will use the current position of the object and the position of the object on the next frame as the time interval to use for motion blur. A value of -1 will use the position of the object on the previous frame and the current position of the object as the time. A value of 0 will generate an interval which begins halfway through the previous frame and ends halfway into the next frame." + }, { "panel": "OBJECT_PT_renderman_object_render", "name": "rman_visibilityCamera", diff --git a/rman_config/config/rman_properties_volume.json b/rman_config/config/rman_properties_volume.json index b0148785..baf15137 100644 --- a/rman_config/config/rman_properties_volume.json +++ b/rman_config/config/rman_properties_volume.json @@ -39,9 +39,9 @@ "editable": true, "default": 1, "widget": "checkbox", - "riattr": "volume:dsominmax", + "primvar": "volume:dsominmax", "page": "", "help": "Currently only used for aggregate volumes, and only for volumes that use an ImplicitField DSO. If set to 1, the DSO may be able to use stored information from the file directly to compute the minimum and maximum values within the volume. This may allow the renderer to avoid shading the volume up front, greatly decreasing time to first pixel. This can only be enabled if the density signal from the volume is used directly, or if the density signal is modulated only by the DSO itself. Any shading modifications of the density signal requires setting this parameter to off." - } + } ] } \ No newline at end of file diff --git a/rman_properties/rman_properties_object/__init__.py b/rman_properties/rman_properties_object/__init__.py index b2b42e95..bac85466 100644 --- a/rman_properties/rman_properties_object/__init__.py +++ b/rman_properties/rman_properties_object/__init__.py @@ -111,8 +111,8 @@ def update_solo(self, context): user_attributes_index: IntProperty(min=-1, default=-1) def get_object_type(self): - if bpy.context.object: - return object_utils._detect_primitive_(bpy.context.object) + if self.id_data: + return object_utils._detect_primitive_(self.id_data) return "" bl_object_type: StringProperty( diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index c3d1d4c5..e3acf001 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -148,17 +148,6 @@ def _export_reference_pose(ob, rm, rixparams, vertex_detail): rixparams.SetNormalDetail('__WNref', rman__WNref, 'vertex') else: rfb_log().error("Number of WNref primvars do not match. Please re-freeze the reference position.") - - ''' - if rman__Pref: - rixparams.SetPointDetail('__Pref', rman__Pref, 'vertex') - if rman__WPref: - rixparams.SetPointDetail('__WPref', rman__WPref, 'vertex') - if rman__Nref: - rixparams.SetNormalDetail('__Nref', rman__Nref, 'vertex') - if rman__WNref: - rixparams.SetNormalDetail('__WNref', rman__WNref, 'vertex') - ''' def export_tangents(ob, geo, rixparams, uvmap="", name=""): # also export the tangent and bitangent vectors @@ -243,27 +232,7 @@ def _get_primvars_(ob, rman_sg_mesh, geo, rixparams): rixparams.SetColorDetail(p.data_name, vattr, detail) rm_scene = rman_sg_mesh.rman_scene.bl_scene.renderman - for prop_name, meta in rm.prop_meta.items(): - if 'primvar' not in meta: - continue - - val = getattr(rm, prop_name) - if not val: - continue - - if 'inheritable' in meta: - if float(val) == meta['inherit_true_value']: - if hasattr(rm_scene, prop_name): - val = getattr(rm_scene, prop_name) - - ri_name = meta['primvar'] - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - property_utils.set_rix_param(rixparams, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm) + property_utils.set_primvar_bl_props(rixparams, rm, inherit_node=rm_scene) class RmanMeshTranslator(RmanTranslator): diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index cd8a7109..5a68a38c 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -4,11 +4,9 @@ from ..rfb_utils import transform_utils from ..rfb_utils import string_utils from ..rfb_utils import scenegraph_utils -from ..rfb_utils import texture_utils -from ..rfb_utils.envconfig_utils import envconfig +from ..rfb_utils import property_utils from ..rfb_logger import rfb_log import json -import os class RmanOpenVDBTranslator(RmanTranslator): def __init__(self, rman_scene): @@ -31,22 +29,14 @@ def export_object_primvars(self, ob, rman_sg_node, sg_node=None): return super().export_object_primvars(ob, rman_sg_node, sg_node=sg_node) - prop_name = 'rman_micropolygonlength_volume' rm = ob.renderman rm_scene = self.rman_scene.bl_scene.renderman - meta = rm.prop_meta[prop_name] - val = getattr(rm, prop_name) - inherit_true_value = meta['inherit_true_value'] - if float(val) == inherit_true_value: - if hasattr(rm_scene, 'rman_micropolygonlength'): - val = getattr(rm_scene, 'rman_micropolygonlength') - try: primvars = sg_node.GetPrimVars() - primvars.SetFloat('dice:micropolygonlength', val) + property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) sg_node.SetPrimVars(primvars) except AttributeError: - rfb_log().debug("Cannot get RtPrimVar for this node") + rfb_log().debug("Cannot get RtPrimVar for this node") def export_deform_sample(self, rman_sg_openvdb, ob, time_sample): pass diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 6e6c97e4..8f5a35c1 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -80,34 +80,11 @@ def export_object_primvars(self, ob, rman_sg_node, sg_node=None): rm_scene = self.rman_scene.bl_scene.renderman try: primvars = sg_node.GetPrimVars() + property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) + sg_node.SetPrimVars(primvars) except AttributeError: rfb_log().debug("Cannot get RtPrimVar for this node") - return - - # set any properties marked primvar in the config file - for prop_name, meta in rm.prop_meta.items(): - if 'primvar' not in meta: - continue - - val = getattr(rm, prop_name) - if not val: - continue - - if 'inheritable' in meta: - if float(val) == meta['inherit_true_value']: - if hasattr(rm_scene, prop_name): - val = getattr(rm_scene, prop_name) - - ri_name = meta['primvar'] - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - property_utils.set_rix_param(primvars, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len) - - sg_node.SetPrimVars(primvars) + return def export_object_id(self, ob, rman_sg_node, ob_inst): if not rman_sg_node.sg_node: @@ -210,6 +187,15 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): if 'riattr' not in meta: continue + conditionalVisOps = meta.get('conditionalVisOps', None) + if conditionalVisOps: + # check conditionalVisOps to see if this riattr applies + # to this object + expr = conditionalVisOps.get('expr', None) + node = rm + if expr and not eval(expr): + continue + ri_name = meta['riattr'] val = getattr(rm, prop_name) if 'inheritable' in meta: @@ -233,7 +219,7 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): if type(val) == str and val.startswith('['): val = eval(val) param_type = meta['renderman_type'] - property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len) + property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) obj_groups_str = "World" obj_groups_str += "," + name diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index c4e5bcf5..d25ff116 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -575,7 +575,8 @@ def poll(cls, context): rm = context.object.renderman if context.object.type in ['LIGHT']: return False - if rm.primitive != 'RI_VOLUME': + rman_type = object_utils._detect_primitive_(context.object) + if rman_type not in ['OPENVDB', 'RI_VOLUME']: return False return (context.object and rd.engine in {'PRMAN_RENDER'}) From 451ce36e66d5e897bdd55fa4ae1d5d4d41abece3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 14 Apr 2022 11:09:14 -0700 Subject: [PATCH 087/278] Forgot to emit some general primvars for curves. --- rman_translators/rman_curve_translator.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index e06d15b4..6497b080 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -156,6 +156,13 @@ def update(self, ob, rman_sg_curve): else: self.update_curve(ob, rman_sg_curve) + def update_primvars(self, ob, primvar): + rm_scene = self.rman_scene.bl_scene.renderman + rm = ob.renderman + property_utils.set_primvar_bl_props(primvar, rm, inherit_node=rm_scene) + rm = ob.data.renderman + property_utils.set_primvar_bl_props(primvar, rm, inherit_node=rm_scene) + def update_bspline_curve(self, ob, rman_sg_curve): P, num_curves, nvertices, widths, index, name = get_bspline_curve(ob.data) num_pts = len(P) @@ -169,6 +176,8 @@ def update_bspline_curve(self, ob, rman_sg_curve): if widths: primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, widths, "vertex") primvar.SetIntegerDetail("index", index, "uniform") + self.update_primvars(ob, primvar) + curves_sg.SetPrimVars(primvar) rman_sg_curve.sg_node.AddChild(curves_sg) @@ -204,6 +213,8 @@ def update_bezier_curve(self, ob, rman_sg_curve): primvar.SetIntegerDetail(self.rman_scene.rman.Tokens.Rix.k_Ri_nvertices, [num_pts], "uniform") if width: primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, width, "vertex") + + self.update_primvars(ob, primvar) curves_sg.SetPrimVars(primvar) rman_sg_curve.sg_node.AddChild(curves_sg) \ No newline at end of file From 6b464245ed2e1c129651aa7b66411f1c2b9c2551 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 14 Apr 2022 15:20:04 -0700 Subject: [PATCH 088/278] Add a constant_width property to the BlHair class, that checks the length of the hair_width list. --- rman_translators/rman_hair_translator.py | 28 ++++++++++-------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/rman_translators/rman_hair_translator.py b/rman_translators/rman_hair_translator.py index 4c835562..0eb60447 100644 --- a/rman_translators/rman_hair_translator.py +++ b/rman_translators/rman_hair_translator.py @@ -10,17 +10,18 @@ class BlHair: - def __init__(self, constant_width=None): + def __init__(self): self.points = [] self.next_points = [] self.vertsArray = [] self.scalpST = [] self.mcols = [] self.nverts = 0 - if constant_width is not None: - self.hair_width = constant_width - else: - self.hair_width = None + self.hair_width = [] + + @property + def constant_width(self): + return (len(self.hair_width) < 2) class RmanHairTranslator(RmanTranslator): def __init__(self, rman_scene): @@ -89,9 +90,9 @@ def update(self, ob, psys, rman_sg_hair): index_nm = 'index' primvar.SetIntegerDetail(index_nm, range(len(bl_curve.vertsArray)), "uniform") - width_detail = "constant" - if isinstance(bl_curve.hair_width, list): - width_detail = "vertex" + width_detail = "vertex" + if bl_curve.constant_width: + width_detail = "constant" primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, bl_curve.hair_width, width_detail) if len(bl_curve.scalpST): @@ -170,10 +171,8 @@ def _get_strands_(self, ob, psys): curve_sets = [] bl_curve = BlHair() if conwidth: - bl_curve.hair_width = base_width - else: - bl_curve.hair_width = [] - + bl_curve.hair_width.append(base_width) + start_idx = 0 if psys.settings.child_type != 'NONE' and num_children > 0: start_idx = num_parents @@ -240,11 +239,6 @@ def _get_strands_(self, ob, psys): curve_sets.append(bl_curve) bl_curve = BlHair() - if not conwidth: - bl_curve.hair_width = [] - else: - bl_curve.hair_width = base_width - if bl_curve.nverts > 0: curve_sets.append(bl_curve) From 27645ff962d7d7518f22194c16a7e712865b3fcf Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 19 Apr 2022 15:05:45 -0700 Subject: [PATCH 089/278] Move the call to export_object_primvars to each translators update() method. This is the better place to put the call, rather than in RmanScene since this is where updates to primvars happens. --- rfb_utils/property_utils.py | 13 ++++---- .../config/rman_properties_object.json | 3 +- rman_scene.py | 1 - rman_scene_sync.py | 2 -- rman_translators/rman_alembic_translator.py | 2 +- rman_translators/rman_blobby_translator.py | 2 +- rman_translators/rman_brickmap_translator.py | 1 + rman_translators/rman_curve_translator.py | 12 ++++--- rman_translators/rman_emitter_translator.py | 1 + rman_translators/rman_empty_translator.py | 3 -- rman_translators/rman_fluid_translator.py | 33 +++---------------- rman_translators/rman_gpencil_translator.py | 8 ++--- rman_translators/rman_light_translator.py | 3 -- .../rman_lightfilter_translator.py | 3 -- rman_translators/rman_mesh_translator.py | 4 +-- rman_translators/rman_nurbs_translator.py | 2 +- rman_translators/rman_openvdb_translator.py | 18 +--------- rman_translators/rman_points_translator.py | 2 +- .../rman_procedural_translator.py | 2 +- rman_translators/rman_quadric_translator.py | 1 + .../rman_runprogram_translator.py | 4 +-- rman_translators/rman_translator.py | 28 ++++++++-------- rman_translators/rman_volume_translator.py | 27 +-------------- 23 files changed, 49 insertions(+), 126 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index e6a34bcc..19f82d5d 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -178,10 +178,11 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar else: # check if we need to emit this parameter. if node != None and not prefs_utils.get_pref('rman_emit_default_params', False): + pname = param_name if prop_name != '': - dflt = get_property_default(node, prop_name) - else: - dflt = get_property_default(node, param_name) + pname = prop_name + dflt = get_property_default(node, pname) + # FIXME/TODO: currently, the python version of RtParamList # doesn't allow us to retrieve existing values. For now, only do the @@ -213,11 +214,11 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar # on default behavior always_write = False prop_meta = getattr(node, 'prop_meta', dict()) - if param_name in node.prop_meta: - meta = prop_meta.get(param_name) + if pname in node.prop_meta: + meta = prop_meta.get(pname) always_write = meta.get('always_write', always_write) if always_write: - rfb_log().debug('Param: %s for Node: %s is marked always_write' % (param_name, node.name)) + rfb_log().debug('Param: %s for Node: %s is marked always_write' % (pname, node.name)) if not always_write and val == dflt: return diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index 5f04f01d..ca7e5f76 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -698,8 +698,7 @@ "type": "float", "default": 10.0, "editable": true, - "inheritable": true, - "inherit_true_value": -1.0, + "always_write": true, "page": "Dicing", "help": "Micropolygon distance in raster space for 'instanceprojection' dicing. Values are expressed in pixel size.", "conditionalVisOps": { diff --git a/rman_scene.py b/rman_scene.py index 9b78eb4b..90493ff5 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -751,7 +751,6 @@ def export_data_block(self, proto_key, ob): rman_sg_node.is_deforming = False translator.update(ob, rman_sg_node) - translator.export_object_primvars(ob, rman_sg_node) if len(ob.particle_systems) > 0: # Deal with any particles now. diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 24aec337..cf6ca975 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -410,7 +410,6 @@ def update_scene(self, context, depsgraph): continue rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) translator.update(o, rman_sg_node) - translator.export_object_primvars(o, rman_sg_node) # material slots could have changed, so we need to double # check that too for k,v in rman_sg_node.instances.items(): @@ -612,7 +611,6 @@ def check_instances(self): translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) translator.update(ob_eval, rman_sg_node) - translator.export_object_primvars(ob_eval, rman_sg_node) self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) diff --git a/rman_translators/rman_alembic_translator.py b/rman_translators/rman_alembic_translator.py index 0c6f9e5f..51819d79 100644 --- a/rman_translators/rman_alembic_translator.py +++ b/rman_translators/rman_alembic_translator.py @@ -48,6 +48,6 @@ def update(self, ob, rman_sg_alembic): abc_args += " -ccw" primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_data, abc_args) - + super().export_object_primvars(ob, primvar) rman_sg_alembic.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_blobby_translator.py b/rman_translators/rman_blobby_translator.py index 5769d331..f01201e2 100644 --- a/rman_translators/rman_blobby_translator.py +++ b/rman_translators/rman_blobby_translator.py @@ -93,5 +93,5 @@ def update(self, ob, rman_sg_blobby): rman_sg_blobby.sg_node.Define(count) primvar.SetIntegerArray(self.rman_scene.rman.Tokens.Rix.k_Ri_code, op, len(op)) primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_floats, tform, len(tform)) - #primvar.SetFloat(self.rman_scene.rman.Tokens.Rix.k_displacementbound_sphere, rm.displacementbound) + super().export_object_primvars(ob, primvar) rman_sg_blobby.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_brickmap_translator.py b/rman_translators/rman_brickmap_translator.py index 964959ca..410d61e4 100644 --- a/rman_translators/rman_brickmap_translator.py +++ b/rman_translators/rman_brickmap_translator.py @@ -26,4 +26,5 @@ def update(self, ob, rman_sg_brickmap): rm = ob.renderman bkm_filepath = string_utils.expand_string(rm.bkm_filepath) primvar.SetString("filename", bkm_filepath) + super().export_object_primvars(ob, primvar) rman_sg_brickmap.sg_node.SetPrimVars(primvar) \ No newline at end of file diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index 6497b080..05e2ff5d 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -131,9 +131,9 @@ def export_deform_sample(self, rman_sg_curve, ob, time_sample): if rman_sg_curve.is_mesh: super().export_deform_sample(rman_sg_curve, ob, time_sample, sg_node=rman_sg_curve.sg_mesh_node) - def export_object_primvars(self, ob, rman_sg_node): - if rman_sg_node.is_mesh: - super().export_object_primvars(ob, rman_sg_node, sg_node=rman_sg_node.sg_mesh_node) + #def export_object_primvars(self, ob, rman_sg_node): + # if rman_sg_node.is_mesh: + # super().export_object_primvars(ob, rman_sg_node, sg_node=rman_sg_node.sg_mesh_node) def update(self, ob, rman_sg_curve): for c in [ rman_sg_curve.sg_node.GetChild(i) for i in range(0, rman_sg_curve.sg_node.GetNumChildren())]: @@ -176,7 +176,8 @@ def update_bspline_curve(self, ob, rman_sg_curve): if widths: primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, widths, "vertex") primvar.SetIntegerDetail("index", index, "uniform") - self.update_primvars(ob, primvar) + #self.update_primvars(ob, primvar) + super().export_object_primvars(ob, primvar) curves_sg.SetPrimVars(primvar) @@ -214,7 +215,8 @@ def update_bezier_curve(self, ob, rman_sg_curve): if width: primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, width, "vertex") - self.update_primvars(ob, primvar) + #self.update_primvars(ob, primvar) + super().export_object_primvars(ob, primvar) curves_sg.SetPrimVars(primvar) rman_sg_curve.sg_node.AddChild(curves_sg) \ No newline at end of file diff --git a/rman_translators/rman_emitter_translator.py b/rman_translators/rman_emitter_translator.py index 181db0b6..a07f3a04 100644 --- a/rman_translators/rman_emitter_translator.py +++ b/rman_translators/rman_emitter_translator.py @@ -71,6 +71,7 @@ def update(self, ob, psys, rman_sg_emitter): else: primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, width, "vertex") + super().export_object_primvars(ob, primvar) sg_emitter_node.SetPrimVars(primvar) # Attach material diff --git a/rman_translators/rman_empty_translator.py b/rman_translators/rman_empty_translator.py index 357a8c68..28a7b7be 100644 --- a/rman_translators/rman_empty_translator.py +++ b/rman_translators/rman_empty_translator.py @@ -10,9 +10,6 @@ class RmanEmptyTranslator(RmanTranslator): def __init__(self, rman_scene): super().__init__(rman_scene) - def export_object_primvars(self, ob, rman_sg_node): - pass - def update_transform(self, ob, rman_sg_group): pass diff --git a/rman_translators/rman_fluid_translator.py b/rman_translators/rman_fluid_translator.py index a937fb6f..8295bc06 100644 --- a/rman_translators/rman_fluid_translator.py +++ b/rman_translators/rman_fluid_translator.py @@ -47,32 +47,6 @@ def export(self, ob, db_name): return rman_sg_fluid - def export_object_primvars(self, ob, rman_sg_node, sg_node=None): - if rman_sg_node.rman_sg_liquid_node: - sg_node = rman_sg_node.rman_sg_liquid_node - super().export_object_primvars(ob, rman_sg_node, sg_node=sg_node) - return - - sg_node = rman_sg_node.rman_sg_volume_node - super().export_object_primvars(ob, rman_sg_node, sg_node=sg_node) - prop_name = 'rman_micropolygonlength_volume' - rm = ob.renderman - rm_scene = self.rman_scene.bl_scene.renderman - meta = rm.prop_meta[prop_name] - val = getattr(rm, prop_name) - inherit_true_value = meta['inherit_true_value'] - if float(val) == inherit_true_value: - if hasattr(rm_scene, 'rman_micropolygonlength'): - val = getattr(rm_scene, 'rman_micropolygonlength') - - try: - primvars = sg_node.GetPrimVars() - primvars.SetFloat('dice:micropolygonlength', val) - sg_node.SetPrimVars(primvars) - except AttributeError: - rfb_log().debug("Cannot get RtPrimVar for this node") - - def export_deform_sample(self, rman_sg_fluid, ob, time_sample): return @@ -150,7 +124,7 @@ def update_fluid_particles(self, ob, rman_sg_fluid, psys, fluid_data): else: primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, P, "vertex") primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, width, "vertex") - + super().export_object_primvars(ob, primvar) sg_node.SetPrimVars(primvar) # Attach material @@ -177,7 +151,8 @@ def update_fluid_openvdb(self, ob, rman_sg_fluid, fluid_data): primvar.SetFloatDetail("density", [], "varying") primvar.SetFloatDetail("flame", [], "varying") - primvar.SetColorDetail("color", [], "varying") + primvar.SetColorDetail("color", [], "varying") + super().export_object_primvars(ob, primvar) rman_sg_fluid.rman_sg_volume_node.SetPrimVars(primvar) attrs = rman_sg_fluid.rman_sg_volume_node.GetAttributes() @@ -199,7 +174,7 @@ def update_fluid(self, ob, rman_sg_fluid, fluid_data): primvar.SetColorDetail("color", [item for index, item in enumerate(fluid_data.color_grid) if index % 4 != 0], "varying") primvar.SetVectorDetail("velocity", fluid_data.velocity_grid, "varying") primvar.SetFloatDetail("temperature", fluid_data.temperature_grid, "varying") - + super().export_object_primvars(ob, primvar) rman_sg_fluid.rman_sg_volume_node.SetPrimVars(primvar) attrs = rman_sg_fluid.rman_sg_volume_node.GetAttributes() diff --git a/rman_translators/rman_gpencil_translator.py b/rman_translators/rman_gpencil_translator.py index 456075d4..7f031ef2 100644 --- a/rman_translators/rman_gpencil_translator.py +++ b/rman_translators/rman_gpencil_translator.py @@ -20,11 +20,6 @@ def __init__(self, rman_scene): super().__init__(rman_scene) self.bl_type = 'GPENCIL' - - def export_object_primvars(self, ob, rman_sg_node): - pass - - def export(self, ob, db_name): prim_type = object_utils._detect_primitive_(ob) @@ -110,6 +105,7 @@ def _create_mesh(self, ob, i, lyr, stroke, rman_sg_gpencil, rman_sg_material, ad primvar.SetIntegerDetail(self.rman_scene.rman.Tokens.Rix.k_Ri_vertices, verts, "facevarying") if st: primvar.SetFloatArrayDetail("st", st, 2, "vertex") + super().export_object_primvars(ob, primvar) mesh_sg.SetPrimVars(primvar) if rman_sg_material: scenegraph_utils.set_material(mesh_sg, rman_sg_material.sg_fill_mat) @@ -145,6 +141,7 @@ def _create_points(self, ob, i, lyr, stroke, rman_sg_gpencil, rman_sg_material, primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, points, "vertex") primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, widths, "vertex") + super().export_object_primvars(ob, primvar) points_sg.SetPrimVars(primvar) # Attach material @@ -199,6 +196,7 @@ def _create_curve(self, ob, i, lyr, stroke, rman_sg_gpencil, rman_sg_material, a primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, widths, "vertex") + super().export_object_primvars(ob, primvar) curves_sg.SetPrimVars(primvar) # Attach material diff --git a/rman_translators/rman_light_translator.py b/rman_translators/rman_light_translator.py index 5e689072..1e7db25c 100644 --- a/rman_translators/rman_light_translator.py +++ b/rman_translators/rman_light_translator.py @@ -40,9 +40,6 @@ def __init__(self, rman_scene): super().__init__(rman_scene) self.bl_type = 'LIGHT' - def export_object_primvars(self, ob, rman_sg_node): - pass - def export_object_attributes(self, ob, rman_sg_node): pass diff --git a/rman_translators/rman_lightfilter_translator.py b/rman_translators/rman_lightfilter_translator.py index 4a7483f5..c9fb3ebd 100644 --- a/rman_translators/rman_lightfilter_translator.py +++ b/rman_translators/rman_lightfilter_translator.py @@ -11,9 +11,6 @@ def __init__(self, rman_scene): super().__init__(rman_scene) self.bl_type = 'LIGHTFILTER' - def export_object_primvars(self, ob, rman_sg_node): - pass - def export_object_attributes(self, ob, rman_sg_node): pass diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index e3acf001..870e4659 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -385,6 +385,8 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): subdiv_scheme = getattr(ob.data.renderman, 'rman_subdiv_scheme', 'none') rman_sg_mesh.subdiv_scheme = subdiv_scheme + super().export_object_primvars(ob, primvar) + if rman_sg_mesh.is_multi_material: material_ids = _get_material_ids(ob, mesh) for mat_id, faces in \ @@ -409,8 +411,6 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): pvars.Inherit(primvar) pvars.SetIntegerArray(self.rman_scene.rman.Tokens.Rix.k_shade_faceset, faces, len(faces)) sg_sub_mesh.SetPrimVars(pvars) - # call export_object_primvars so we can get things like displacement bound - super().export_object_primvars(ob, rman_sg_mesh, sg_sub_mesh) scenegraph_utils.set_material(sg_sub_mesh, sg_material.sg_node) sg_node.AddChild(sg_sub_mesh) rman_sg_mesh.multi_material_children.append(sg_sub_mesh) diff --git a/rman_translators/rman_nurbs_translator.py b/rman_translators/rman_nurbs_translator.py index 7d801b9d..7e2db3df 100644 --- a/rman_translators/rman_nurbs_translator.py +++ b/rman_translators/rman_nurbs_translator.py @@ -260,5 +260,5 @@ def update(self, ob, rman_sg_nurbs): primvar.SetHpointDetail(self.rman_scene.rman.Tokens.Rix.k_Pw, P, "vertex") primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_uknot, uknots, len(uknots)) primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_vknot, vknots, len(vknots)) - + super().export_object_primvars(ob, primvar) rman_sg_nurbs.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index 5a68a38c..4ddd1b7b 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -21,23 +21,6 @@ def export(self, ob, db_name): return rman_sg_openvdb - def export_object_primvars(self, ob, rman_sg_node, sg_node=None): - if not sg_node: - sg_node = rman_sg_node.sg_node - - if not sg_node: - return - - super().export_object_primvars(ob, rman_sg_node, sg_node=sg_node) - rm = ob.renderman - rm_scene = self.rman_scene.bl_scene.renderman - try: - primvars = sg_node.GetPrimVars() - property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) - sg_node.SetPrimVars(primvars) - except AttributeError: - rfb_log().debug("Cannot get RtPrimVar for this node") - def export_deform_sample(self, rman_sg_openvdb, ob, time_sample): pass @@ -107,4 +90,5 @@ def update(self, ob, rman_sg_openvdb): scenegraph_utils.export_vol_aggregate(self.rman_scene.bl_scene, primvar, ob) primvar.SetInteger("volume:dsominmax", rm.volume_dsominmax) + super().export_object_primvars(ob, primvar) rman_sg_openvdb.sg_node.SetPrimVars(primvar) \ No newline at end of file diff --git a/rman_translators/rman_points_translator.py b/rman_translators/rman_points_translator.py index 2b322dff..bdf061da 100644 --- a/rman_translators/rman_points_translator.py +++ b/rman_translators/rman_points_translator.py @@ -69,7 +69,7 @@ def update(self, ob, rman_sg_points, input_mesh=None): primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, P, "vertex") primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_constantwidth, rm.primitive_point_width, "constant") - + super().export_object_primvars(ob, primvar) rman_sg_points.sg_node.SetPrimVars(primvar) if not input_mesh: diff --git a/rman_translators/rman_procedural_translator.py b/rman_translators/rman_procedural_translator.py index 4d1facab..d20f7b97 100644 --- a/rman_translators/rman_procedural_translator.py +++ b/rman_translators/rman_procedural_translator.py @@ -28,5 +28,5 @@ def update(self, ob, rman_sg_procedural): primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_dsoname, path_dso) primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_data, rm.path_dso_initial_data ) primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_bound, bounds, 6) - + super().export_object_primvars(ob, primvar) rman_sg_procedural.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_quadric_translator.py b/rman_translators/rman_quadric_translator.py index 332314f3..be848d4d 100644 --- a/rman_translators/rman_quadric_translator.py +++ b/rman_translators/rman_quadric_translator.py @@ -62,4 +62,5 @@ def update(self, ob, rman_sg_quadric): primvar.SetFloat(self.rman_scene.rman.Tokens.Rix.k_Ri_phimax, rm.quadric_phimax) primvar.SetFloat(self.rman_scene.rman.Tokens.Rix.k_Ri_thetamax, rm.quadric_sweepangle) + super().export_object_primvars(ob, primvar) rman_sg_quadric.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_runprogram_translator.py b/rman_translators/rman_runprogram_translator.py index bfcffd25..9e069dab 100644 --- a/rman_translators/rman_runprogram_translator.py +++ b/rman_translators/rman_runprogram_translator.py @@ -25,8 +25,8 @@ def update(self, ob, rman_sg_runprogram): bounds = (-100000, 100000, -100000, 100000, -100000, 100000 ) primvar = rman_sg_runprogram.sg_node.GetPrimVars() - primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_filename, runprogram_path) + primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_filename, path_runprogram) primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_data, rm.runprogram_args) primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_bound, bounds, 6) - + super().export_object_primvars(ob, primvar) rman_sg_runprogram.sg_node.SetPrimVars(primvar) diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 8f5a35c1..621c756f 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -68,23 +68,21 @@ def export_transform(self, ob, sg_node): v = transform_utils.convert_matrix(m) - sg_node.SetTransform( v ) - - def export_object_primvars(self, ob, rman_sg_node, sg_node=None): - if not sg_node: - sg_node = rman_sg_node.sg_node - - if not sg_node: - return + sg_node.SetTransform( v ) + + def export_object_primvars(self, ob, primvars): + ''' + This method should be called by subclasses of RmanTranslator + in their update() methods, if they are setting any primvars. This + sets things like displacement bound and micropolygon length. + + Args: + ob (bpy.types.Object) - Blender Object + primvars (RtPrimVars) - primitive variables + ''' rm = ob.renderman rm_scene = self.rman_scene.bl_scene.renderman - try: - primvars = sg_node.GetPrimVars() - property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) - sg_node.SetPrimVars(primvars) - except AttributeError: - rfb_log().debug("Cannot get RtPrimVar for this node") - return + property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) def export_object_id(self, ob, rman_sg_node, ob_inst): if not rman_sg_node.sg_node: diff --git a/rman_translators/rman_volume_translator.py b/rman_translators/rman_volume_translator.py index 1e8a7fb1..ce6d5e66 100644 --- a/rman_translators/rman_volume_translator.py +++ b/rman_translators/rman_volume_translator.py @@ -19,32 +19,6 @@ def export(self, ob, db_name): return rman_sg_volume - def export_object_primvars(self, ob, rman_sg_node, sg_node=None): - if not sg_node: - sg_node = rman_sg_node.sg_node - - if not sg_node: - return - - super().export_object_primvars(ob, rman_sg_node, sg_node=sg_node) - prop_name = 'rman_micropolygonlength_volume' - rm = ob.renderman - rm_scene = self.rman_scene.bl_scene.renderman - meta = rm.prop_meta[prop_name] - val = getattr(rm, prop_name) - inherit_true_value = meta['inherit_true_value'] - if float(val) == inherit_true_value: - if hasattr(rm_scene, 'rman_micropolygonlength'): - val = getattr(rm_scene, 'rman_micropolygonlength') - - try: - primvars = sg_node.GetPrimVars() - primvars.SetFloat('dice:micropolygonlength', val) - sg_node.SetPrimVars(primvars) - except AttributeError: - rfb_log().debug("Cannot get RtPrimVar for this node") - - def export_deform_sample(self, rman_sg_volume, ob, time_sample): pass @@ -59,6 +33,7 @@ def update(self, ob, rman_sg_volume): primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, bound_box, 6) else: primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, transform_utils.convert_ob_bounds(ob.bound_box), 6) + super().export_object_primvars(ob, primvar) rman_sg_volume.sg_node.SetPrimVars(primvar) attrs = rman_sg_volume.sg_node.GetAttributes() From ae325cff6aaf8400b0426366bc91b1aa8d765aff Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 20 Apr 2022 15:00:49 -0700 Subject: [PATCH 090/278] Grey out the blender_ipr_optix_denoiser option when viewport rendering is turned on. --- rman_ui/rman_ui_viewport.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index a6915882..8ef830c9 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -884,9 +884,11 @@ def draw(self, context): col.prop(prefs, 'rman_viewport_draw_progress') if prefs.rman_viewport_draw_progress: col.prop(prefs, 'rman_viewport_progress_color') - col.prop(prefs, 'draw_ipr_text') if rm.current_platform != ("macOS"): + col = layout.column(align=True) col.prop(rm, 'blender_ipr_optix_denoiser') + if rman_render.rman_interactive_running: + col.enabled = False if rm.current_platform != ("macOS") and rm.has_xpu_license: col = layout.column(align=True) From 0f6517960daa9a8a67c04c70348fa4a16c35717b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 26 Apr 2022 09:00:13 -0700 Subject: [PATCH 091/278] Use foreach_set when setting the pixels of the images saved as snapshaots and from preview renders. --- rman_render.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rman_render.py b/rman_render.py index ce5e2dac..9badfd06 100644 --- a/rman_render.py +++ b/rman_render.py @@ -27,6 +27,7 @@ from .rfb_utils import scene_utils from .rfb_utils import transform_utils from .rfb_utils.prefs_utils import get_pref +from .rfb_utils.timer_utils import time_this # config from .rman_config import __RFB_CONFIG_DICT__ as rfb_config @@ -620,7 +621,7 @@ def start_render(self, depsgraph, for_background=False): try: bl_image.use_generated_float = True bl_image.filepath_raw = filepath - bl_image.pixels = buffer + bl_image.pixels.foreach_set(buffer) bl_image.file_format = 'OPEN_EXR' bl_image.update() bl_image.save() @@ -1335,6 +1336,7 @@ def _get_buffer(self, width, height, image_num=0, num_channels=-1, back_fill=Tru rfb_log().debug("Could not get buffer: %s" % str(e)) return None + @time_this def save_viewport_snapshot(self, frame=1): if not self.rman_is_viewport_rendering: return @@ -1351,7 +1353,7 @@ def save_viewport_snapshot(self, frame=1): nm = 'rman_viewport_snapshot__%d' % len(bpy.data.images) nm = string_utils.expand_string(nm, frame=frame) img = bpy.data.images.new(nm, width, height, float_buffer=True, alpha=True) - img.pixels = pixels + img.pixels.foreach_set(pixels) img.update() def update_scene(self, context, depsgraph): From 502e2a9bd1892814e985abaf037e567cf741d570 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 26 Apr 2022 11:16:47 -0700 Subject: [PATCH 092/278] Use the drawcallback function for XPU viewport renders. We add a RFB_XPU_SLOW_MODE debug env var, in case we want to go back to the previous behavior. --- rman_render.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rman_render.py b/rman_render.py index 9badfd06..8298b192 100644 --- a/rman_render.py +++ b/rman_render.py @@ -151,7 +151,7 @@ def draw_threading_func(db): # if the viewport is not rendering, stop IPR db.del_bl_engine() break - if db.rman_is_xpu: + if db.xpu_slow_mode: if db.has_buffer_updated(): try: db.bl_engine.tag_redraw() @@ -280,6 +280,7 @@ def __init__(self): self.deleting_bl_engine = threading.Lock() self.stop_render_mtx = threading.Lock() self.bl_viewport = None + self.xpu_slow_mode = False self._start_prman_begin() @@ -445,6 +446,7 @@ def reset(self): self.rman_is_xpu = False self.rman_is_refining = False self.bl_viewport = None + self.xpu_slow_mode = False def start_render(self, depsgraph, for_background=False): @@ -925,6 +927,8 @@ def start_interactive_render(self, context, depsgraph): rendervariant = scene_utils.get_render_variant(self.bl_scene) scene_utils.set_render_variant_config(self.bl_scene, config, render_config) self.rman_is_xpu = (rendervariant == 'xpu') + if self.rman_is_xpu: + self.xpu_slow_mode = envconfig().getenv('RFB_XPU_SLOW_MODE', default=False) self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) @@ -949,9 +953,10 @@ def start_interactive_render(self, context, depsgraph): if render_into_org != '': rm.render_ipr_into = render_into_org - if not self.rman_is_xpu: - # for now, we only set the redraw callback for RIS + if not self.xpu_slow_mode: self.set_redraw_func() + else: + rfb_log().debug("XPU slow mode enabled.") # start a thread to periodically call engine.tag_redraw() __DRAW_THREAD__ = threading.Thread(target=draw_threading_func, args=(self, )) __DRAW_THREAD__.start() From 402822bafc84447982531166788b4cb8e1265051 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 26 Apr 2022 15:19:14 -0700 Subject: [PATCH 093/278] Rearrange some of the logging when cameras are updated during IPR. --- rman_scene_sync.py | 7 ++----- rman_translators/rman_camera_translator.py | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index cf6ca975..73789c6d 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -233,8 +233,8 @@ def camera_updated(self, ob_update): # we deal with main camera transforms in view_draw if rman_sg_camera == self.rman_scene.main_camera: return - rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) - translator._update_render_cam_transform(ob, rman_sg_camera) + if translator._update_render_cam_transform(ob, rman_sg_camera): + rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) def check_particle_instancer(self, ob_update, psys): @@ -469,15 +469,12 @@ def update_scene(self, context, depsgraph): # These types need special handling if rman_type == 'EMPTY': - rfb_log().debug("\tEmpty: %s Updated" % obj.id.name) self.update_empty(obj) continue if rman_type == 'LIGHTFILTER': - rfb_log().debug("\tLight Filter: %s Updated" % obj.id.name) self.light_filter_updated(obj) continue if ob.type in ['CAMERA']: - rfb_log().debug("\tCamera updated: %s" % obj.id.name) self.camera_updated(obj) continue diff --git a/rman_translators/rman_camera_translator.py b/rman_translators/rman_camera_translator.py index 0db09680..99b3b99c 100644 --- a/rman_translators/rman_camera_translator.py +++ b/rman_translators/rman_camera_translator.py @@ -50,7 +50,6 @@ def _update_viewport_transform(self, rman_sg_camera): def _update_render_cam_transform(self, ob, rman_sg_camera, index=0, seg=0.0): - cam = ob.data mtx = ob.matrix_world # normalize the matrix @@ -61,7 +60,7 @@ def _update_render_cam_transform(self, ob, rman_sg_camera, index=0, seg=0.0): v = transform_utils.convert_matrix(mtx) if rman_sg_camera.cam_matrix == v: - return + return False rman_sg_camera.cam_matrix = v if rman_sg_camera.is_transforming: @@ -69,6 +68,7 @@ def _update_render_cam_transform(self, ob, rman_sg_camera, index=0, seg=0.0): else: rman_sg_camera.sg_node.SetTransform( v ) + return True def update_transform(self, ob, rman_sg_camera, index=0, seg=0): if self.rman_scene.is_viewport_render: From c04802cb162b8456f96975b5c17477c6ca9fc5a5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 27 Apr 2022 10:54:25 -0700 Subject: [PATCH 094/278] Some improvements to get_buffer: * Switch to numpy arrays for all buffer manipulations. * use round() instead of int() when dealing with render borders --- rman_render.py | 123 ++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/rman_render.py b/rman_render.py index 8298b192..c9620dfc 100644 --- a/rman_render.py +++ b/rman_render.py @@ -603,8 +603,9 @@ def start_render(self, depsgraph, for_background=False): as_flat=False, back_fill=False, render=render) - if buffer: - rp.rect = buffer + if buffer is None: + continue + rp.rect = buffer if self.bl_engine: self.bl_engine.update_result(result) @@ -618,19 +619,23 @@ def start_render(self, depsgraph, for_background=False): for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): filepath = dspy_dict['displays'][dspy_nm]['filePath'] buffer = self._get_buffer(width, height, image_num=i, as_flat=True) - if buffer: - bl_image = bpy.data.images.new(dspy_nm, width, height) - try: - bl_image.use_generated_float = True - bl_image.filepath_raw = filepath - bl_image.pixels.foreach_set(buffer) - bl_image.file_format = 'OPEN_EXR' - bl_image.update() - bl_image.save() - except: - pass - finally: - bpy.data.images.remove(bl_image) + if buffer is None: + continue + + bl_image = bpy.data.images.new(dspy_nm, width, height) + try: + if isinstance(buffer, numpy.ndarray): + buffer = buffer.tolist() + bl_image.use_generated_float = True + bl_image.filepath_raw = filepath + bl_image.pixels.foreach_set(buffer) + bl_image.file_format = 'OPEN_EXR' + bl_image.update() + bl_image.save() + except: + pass + finally: + bpy.data.images.remove(bl_image) if not was_connected and self.stats_mgr.is_connected(): # if stats were not started before rendering, disconnect @@ -1259,36 +1264,28 @@ def _get_buffer(self, width, height, image_num=0, num_channels=-1, back_fill=Tru try: buffer = numpy.array(f(ctypes.c_size_t(image_num)).contents) - pixels = list() - - # we need to flip the image - # also, Blender is expecting a 4 channel image if as_flat: - if num_channels == 4: - return buffer.tolist() + if (num_channels == 4) or not back_fill: + return buffer else: + p_pos = 0 + pixels = numpy.ones(width*height*4, dtype=numpy.float32) for y in range(0, height): i = (width * y * num_channels) for x in range(0, width): j = i + (num_channels * x) if num_channels == 3: - pixels.append(buffer[j]) - pixels.append(buffer[j+1]) - pixels.append(buffer[j+2]) - pixels.append(1.0) + pixels[p_pos:p_pos+3] = buffer[j:j+3] elif num_channels == 2: - pixels.append(buffer[j]) - pixels.append(buffer[j+1]) - pixels.append(1.0) - pixels.append(1.0) + pixels[p_pos:p_pos+2] = buffer[j:j+2] elif num_channels == 1: - pixels.append(buffer[j]) - pixels.append(buffer[j]) - pixels.append(buffer[j]) - pixels.append(1.0) - return pixels + pixels[p_pos] = buffer[j] + pixels[p_pos+1] = buffer[j] + pixels[p_pos+2] = buffer[j] + p_pos += 4 + return pixels else: if render and render.use_border: start_x = 0 @@ -1298,45 +1295,45 @@ def _get_buffer(self, width, height, image_num=0, num_channels=-1, back_fill=Tru if render.border_min_y > 0.0: - start_y = int(height * (render.border_min_y))-1 + start_y = round(height * render.border_min_y)-1 if render.border_max_y > 0.0: - end_y = int(height * (render.border_max_y))-1 + end_y = round(height * render.border_max_y)-1 if render.border_min_x > 0.0: - start_x = int(width * render.border_min_x)-1 + start_x = round(width * render.border_min_x)-1 if render.border_max_x < 1.0: - end_x = int(width * render.border_max_x)-2 + end_x = round(width * render.border_max_x)-2 # return the buffer as a list of lists + if back_fill: + pixels = numpy.ones( ((end_x-start_x)*(end_y-start_y), 4), dtype=numpy.float32 ) + else: + pixels = numpy.zeros( ((end_x-start_x)*(end_y-start_y), num_channels) ) + p_pos = 0 for y in range(start_y, end_y): i = (width * y * num_channels) for x in range(start_x, end_x): j = i + (num_channels * x) - if not back_fill: - pixels.append(buffer[j:j+num_channels]) - continue - - if num_channels == 4: - pixels.append(buffer[j:j+4]) - continue - - pixel = [1.0] * num_channels - pixel[0] = buffer[j] - if num_channels == 3: - pixel[1] = buffer[j+1] - pixel[2] = buffer[j+2] - elif num_channels == 2: - pixel[1] = buffer[j+1] - elif num_channels == 1: - pixel[1] = buffer[j] - pixel[2] = buffer[j] - - pixels.append(pixel) + if (num_channels==4) or not back_fill: + # just slice + pixels[p_pos] = buffer[j:j+num_channels] + else: + pixels[p_pos][0] = buffer[j] + if num_channels == 3: + pixels[p_pos][1] = buffer[j+1] + pixels[p_pos][2] = buffer[j+2] + elif num_channels == 2: + pixels[p_pos][1] = buffer[j+1] + elif num_channels == 1: + pixels[p_pos][1] = buffer[j] + pixels[p_pos][2] = buffer[j] + + p_pos += 1 return pixels else: - buffer = numpy.reshape(buffer, (-1, num_channels)) - return buffer.tolist() + buffer.shape = (-1, num_channels) + return buffer except Exception as e: rfb_log().debug("Could not get buffer: %s" % str(e)) return None @@ -1351,13 +1348,15 @@ def save_viewport_snapshot(self, frame=1): height = int(self.viewport_res_y * res_mult) pixels = self._get_buffer(width, height) - if not pixels: + if pixels is None: rfb_log().error("Could not save snapshot.") return nm = 'rman_viewport_snapshot__%d' % len(bpy.data.images) nm = string_utils.expand_string(nm, frame=frame) - img = bpy.data.images.new(nm, width, height, float_buffer=True, alpha=True) + img = bpy.data.images.new(nm, width, height, float_buffer=True, alpha=True) + if isinstance(pixels, numpy.ndarray): + pixels = pixels.tolist() img.pixels.foreach_set(pixels) img.update() From 7d42a8732f7fdf5df22f4723c3aa6e5209184c43 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 29 Apr 2022 12:02:02 -0700 Subject: [PATCH 095/278] Don't register the holdout displays as render passes in Blender. --- __init__.py | 2 +- rfb_utils/display_utils.py | 4 ++-- rman_render.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index 4dd7d488..569bd508 100644 --- a/__init__.py +++ b/__init__.py @@ -161,7 +161,7 @@ def update_render_passes(self, scene=None, renderlayer=None): return self.rman_render.rman_scene.bl_scene = scene - dspy_dict = display_utils.get_dspy_dict(self.rman_render.rman_scene) + dspy_dict = display_utils.get_dspy_dict(self.rman_render.rman_scene, include_holdouts=False) self.register_pass(scene, renderlayer, "Combined", 4, "RGBA", 'COLOR') for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): if i == 0: diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 2c0e8048..645e0593 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -563,7 +563,7 @@ def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens) 'params': dspy_params, 'dspyDriverParams': None} -def get_dspy_dict(rman_scene, expandTokens=True): +def get_dspy_dict(rman_scene, expandTokens=True, include_holdouts=True): """ Create a dictionary of display channels and displays. The layout: @@ -640,7 +640,7 @@ def get_dspy_dict(rman_scene, expandTokens=True): # We're using blender's layering system _set_blender_dspy_dict(layer, dspys_dict, display_driver, rman_scene, expandTokens, do_optix_denoise=do_optix_denoise) - if rm.do_holdout_matte != "OFF": + if rm.do_holdout_matte != "OFF" and include_holdouts: _set_rman_holdouts_dspy_dict(dspys_dict, display_driver, rman_scene, expandTokens) return dspys_dict diff --git a/rman_render.py b/rman_render.py index c9620dfc..b26ec01a 100644 --- a/rman_render.py +++ b/rman_render.py @@ -536,7 +536,7 @@ def start_render(self, depsgraph, for_background=False): render_cmd = self._append_render_cmd(render_cmd) self.sg_scene.Render(render_cmd) if self.rman_render_into == 'blender': - dspy_dict = display_utils.get_dspy_dict(self.rman_scene) + dspy_dict = display_utils.get_dspy_dict(self.rman_scene, include_holdouts=False) render = self.rman_scene.bl_scene.render render_view = self.bl_engine.active_view_get() From ecf2573620feb06cb713430f3b202f30399ac85c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 2 May 2022 09:53:39 -0700 Subject: [PATCH 096/278] Add a pre and post render handles to deal with Blender always wanting to write an image when we render. * in the non-background case, change the filepath to match our beauty file path * in the background case, we use the post render handler to remove the image written. --- rfb_utils/display_utils.py | 59 ++++++++++++++++++++++---------- rfb_utils/string_expr.py | 1 + rfb_utils/string_utils.py | 1 + rman_handlers/__init__.py | 70 ++++++++++++++++++++++++++++++++++++++ rman_render.py | 10 +++++- rman_spool.py | 25 +++++++------- 6 files changed, 135 insertions(+), 31 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 645e0593..719f1fd9 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -9,29 +9,36 @@ import bpy import os import getpass +import re __BLENDER_TO_RMAN_DSPY__ = { 'TIFF': 'tiff', 'TARGA': 'targa', 'TARGA_RAW': 'targa', 'OPEN_EXR': 'openexr', 'PNG': 'png'} +__RMAN_TO_BLENDER__ = { 'tiff': 'TIFF', 'targa': 'TARGA', 'openexr':'OPEN_EXR', 'png':'PNG'} -def get_channel_name(aov, layer_name): - aov_name = aov.name.replace(' ', '') - aov_channel_name = aov.channel_name - if not aov.aov_name or not aov.channel_name: - return '' - elif aov.aov_name == "color rgba": - aov_channel_name = "Ci,a" - # Remaps any color lpe channel names to a denoise friendly one - elif aov_name in channel_name_map.keys(): - aov_channel_name = '%s_%s_%s' % ( - channel_name_map[aov_name], aov_name, layer_name) - - elif aov.aov_name == "color custom_lpe": - aov_channel_name = aov.name +def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False): + view_layer = bpy.context.view_layer + rm_rl = None + if view_layer.renderman.use_renderman: + rm_rl = view_layer.renderman + rm = bl_scene.renderman + string_utils.set_var('scene', bl_scene.name.replace(' ', '_')) + string_utils.set_var('layer', view_layer.name.replace(' ', '_')) + + filePath = rm.path_beauty_image_output + if use_blender_frame: + filePath = re.sub(r'<[f|F]\d*>', '####', filePath) + if rm_rl: + file_format = bl_scene.render.image_settings.file_format + display_driver = __BLENDER_TO_RMAN_DSPY__.get(file_format, 'openexr') else: - aov_channel_name = '%s_%s' % ( - aov_name, layer_name) + aov = rm_rl.custom_aovs[0] + display_driver = aov.displaydriver - return aov_channel_name + if expand_tokens: + filePath = string_utils.expand_string(filePath, + display=display_driver, + asFilePath=True) + return filePath def _default_dspy_params(): d = {} @@ -561,8 +568,24 @@ def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens) 'camera': None, 'bake_mode': None, 'params': dspy_params, - 'dspyDriverParams': None} + 'dspyDriverParams': None} +def any_dspys_denoise(view_layer): + if view_layer.renderman.use_renderman: + rm_rl = view_layer.renderman + if rm_rl: + for aov in rm_rl.custom_aovs: + if aov.denoise: + return True + return False + +def get_renderman_layer(view_layer=None): + if view_layer is None: + view_layer = bpy.context.view_layer + if view_layer.renderman.use_renderman: + return view_layer.renderman + return None + def get_dspy_dict(rman_scene, expandTokens=True, include_holdouts=True): """ Create a dictionary of display channels and displays. The layout: diff --git a/rfb_utils/string_expr.py b/rfb_utils/string_expr.py index c2e39d5e..e36f20d3 100644 --- a/rfb_utils/string_expr.py +++ b/rfb_utils/string_expr.py @@ -226,6 +226,7 @@ def expand(self, expr, objTokens={}, asFilePath=False): # get the real path result = filepath_utils.get_real_path(result) + result = result.replace(' ', '_') dirname = os.path.dirname(result) if not os.path.exists(dirname): diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 34554eaf..8411bd09 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -129,6 +129,7 @@ def _resetStringConverter(): # get the real path if string and asFilePath and os.path.isabs(string): string = filepath_utils.get_real_path(string) + string = string.replace(' ', '_') dirname = os.path.dirname(string) if not os.path.exists(dirname): try: diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index 70794025..fd0d99c9 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -4,6 +4,18 @@ from ..rfb_utils import upgrade_utils from bpy.app.handlers import persistent import bpy +import os +import re +import sys + + +__ORIGINAL_BL_FILEPATH__ = None +__ORIGINAL_BL_FILE_FORMAT__ = None +__BL_TMP_FILE__ = None +if sys.platform == ("win32"): + __BL_TMP_DIR__ = 'C:/tmp' +else: + __BL_TMP_DIR__ = '/tmp' @persistent def rman_load_post(bl_scene): @@ -28,6 +40,52 @@ def rman_save_post(bl_scene): def texture_despgraph_handler(bl_scene, depsgraph): texture_utils.depsgraph_handler(bl_scene, depsgraph) +@persistent +def render_pre(bl_scene): + ''' + render_pre handler that changes the Blender filepath attribute + to match our filename output format. In the case of background + mode, set it to a temporary filename. The temporary filename will + get removed in the render_post handler. + ''' + global __ORIGINAL_BL_FILEPATH__ + global __ORIGINAL_BL_FILE_FORMAT__ + global __BL_TMP_FILE__ + global __BL_TMP_DIR__ + from ..rfb_utils import display_utils + + __ORIGINAL_BL_FILEPATH__ = bl_scene.render.filepath + __ORIGINAL_BL_FILE_FORMAT__ = bl_scene.render.image_settings.file_format + if not bpy.app.background: + filePath = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True) + bl_scene.render.filepath = filePath + bl_scene.render.image_settings.file_format = 'OPEN_EXR' + else: + __BL_TMP_FILE__ = os.path.join(__BL_TMP_DIR__, '####.png') + bl_scene.render.filepath = __BL_TMP_FILE__ + bl_scene.render.image_settings.file_format = 'PNG' + +@persistent +def render_post(bl_scene): + ''' + render_post handler that puts the Blender filepath attribute back + to its original value. Also, remove the temporary output file if + it exists. + ''' + + global __ORIGINAL_BL_FILEPATH__ + global __ORIGINAL_BL_FILE_FORMAT__ + global __BL_TMP_FILE__ + from ..rfb_utils import display_utils + + bl_scene.render.filepath = __ORIGINAL_BL_FILEPATH__ + bl_scene.render.image_settings.file_format = __ORIGINAL_BL_FILE_FORMAT__ + if __BL_TMP_FILE__: + filePath = re.sub(r'####', '%04d' % bl_scene.frame_current, __BL_TMP_FILE__) + if os.path.exists(filePath): + os.remove(filePath) + __BL_TMP_FILE__ = None + def register(): # load_post handler @@ -46,6 +104,12 @@ def register(): if texture_despgraph_handler not in bpy.app.handlers.depsgraph_update_post: bpy.app.handlers.depsgraph_update_post.append(texture_despgraph_handler) + if render_pre not in bpy.app.handlers.render_pre: + bpy.app.handlers.render_pre.append(render_pre) + + if render_post not in bpy.app.handlers.render_post: + bpy.app.handlers.render_post.append(render_post) + def unregister(): if rman_load_post in bpy.app.handlers.load_post: @@ -60,5 +124,11 @@ def unregister(): if texture_despgraph_handler in bpy.app.handlers.depsgraph_update_post: bpy.app.handlers.depsgraph_update_post.remove(texture_despgraph_handler) + if render_pre in bpy.app.handlers.render_pre: + bpy.app.handlers.render_pre.remove(render_pre) + + if render_post in bpy.app.handlers.render_post: + bpy.app.handlers.render_post.remove(render_post) + from . import rman_it_handlers rman_it_handlers.remove_ipr_to_it_handlers() diff --git a/rman_render.py b/rman_render.py index b26ec01a..b8b22138 100644 --- a/rman_render.py +++ b/rman_render.py @@ -615,13 +615,21 @@ def start_render(self, depsgraph, for_background=False): self.bl_engine.end_result(result) # Try to save out the displays out to disk. This matches - # Cycles behavior + # Cycles behavior for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): filepath = dspy_dict['displays'][dspy_nm]['filePath'] buffer = self._get_buffer(width, height, image_num=i, as_flat=True) if buffer is None: continue + if i == 0: + # if this is the beauty, add the substring 'beauty_raw' + # to indicate this is the "raw" beauty + # i.e.: it does not include the result of the Blender + # compositor + toks = os.path.splitext(filepath) + filepath = '%s_%s%s' % (toks[0], 'beauty_raw.exr') + bl_image = bpy.data.images.new(dspy_nm, width, height) try: if isinstance(buffer, numpy.ndarray): diff --git a/rman_spool.py b/rman_spool.py index 1ebf4d82..b0de7bba 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -2,7 +2,6 @@ import os import getpass import socket -import datetime import bpy from .rfb_utils import string_utils from .rfb_utils.envconfig_utils import envconfig @@ -21,6 +20,7 @@ def __init__(self, rman_render, rman_scene, depsgraph): self.rman_scene = rman_scene self.is_localqueue = True self.is_tractor = False + self.any_denoise = False if depsgraph: self.bl_scene = depsgraph.scene_eval self.depsgraph = depsgraph @@ -84,12 +84,12 @@ def add_prman_render_task(self, parentTask, title, threads, rib, img): def add_blender_render_task(self, frame, parentTask, title, bl_filename, img): rm = self.bl_scene.renderman - out_dir = '' task = author.Task() task.title = title - if img: - task.preview = 'sho %s' % str(img) + if img: + img_expanded = string_utils.expand_string(img, frame=frame, asFilePath=True) + task.preview = 'sho %s' % img_expanded command = author.Command(local=False, service="PixarRender") bl_blender_path = bpy.app.binary_path @@ -107,11 +107,14 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, start, last, by, bl_filename): rm = self.bl_scene.renderman - + self.any_denoise = display_utils.any_dspys_denoise(self.rman_scene.bl_view_layer) + img = None + if not self.any_denoise: + dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) + img = dspys_dict['displays']['beauty']['filePath'] + if anim is False: - - img_expanded = '' - + frametasktitle = ("%s Frame: %d " % (tasktitle, int(start))) frametask = author.Task() @@ -121,7 +124,7 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, prmantasktitle = "%s (render)" % frametasktitle self.add_blender_render_task(start, frametask, prmantasktitle, - bl_filename, img_expanded) + bl_filename, img) parent_task.addChild(frametask) @@ -136,13 +139,11 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, for iframe in range(int(start), int(last + 1), int(by)): - img_expanded = '' - prmantasktitle = ("%s Frame: %d (blender)" % (tasktitle, int(iframe))) self.add_blender_render_task(iframe, renderframestask, prmantasktitle, - bl_filename, img_expanded) + bl_filename, img) parent_task.addChild(renderframestask) From 6337695c455364b355eb309052545b901daf940e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 2 May 2022 12:30:06 -0700 Subject: [PATCH 097/278] * add some missing filters and help text for display channel pixel filter setting. --- rfb_utils/display_utils.py | 3 +-- .../config/rman_properties_dspychan.json | 16 +++++++------- rman_ui/rman_ui_aovs.py | 22 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 719f1fd9..0d2e285e 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -332,8 +332,7 @@ def _add_chan_to_dpsychan_list(rm, rm_rl, dspys_dict, chan): d[u'remap_c'] = { 'type': u'float', 'value': chan.remap_c} d[u'exposure'] = { 'type': u'float2', 'value': [chan.exposure_gain, chan.exposure_gamma] } - if rm.hider_pixelFilterMode != 'importance': - # per channel filter does not work in importance mode + if chan.chan_pixelfilter != 'default': d[u'filter'] = {'type': u'string', 'value': chan.chan_pixelfilter} d[u'filterwidth'] = { 'type': u'float2', 'value': [chan.chan_pixelfilter_x, chan.chan_pixelfilter_y]} diff --git a/rman_config/config/rman_properties_dspychan.json b/rman_config/config/rman_properties_dspychan.json index 582927d2..2ba42e09 100644 --- a/rman_config/config/rman_properties_dspychan.json +++ b/rman_config/config/rman_properties_dspychan.json @@ -42,7 +42,7 @@ { "panel": "RENDER_PT_layer_custom_aovs", "name": "remap_a", - "label": "a", + "label": "Break Point", "type": "float", "default": 0.0, "page": "Remap Settings", @@ -56,7 +56,7 @@ { "panel": "RENDER_PT_layer_custom_aovs", "name": "remap_b", - "label": "b", + "label": "Max Value", "type": "float", "default": 0.0, "page": "Remap Settings", @@ -70,7 +70,7 @@ { "panel": "RENDER_PT_layer_custom_aovs", "name": "remap_c", - "label": "c", + "label": "Smoothness", "type": "float", "default": 0.0, "page": "Remap Settings", @@ -86,11 +86,11 @@ "name": "chan_pixelfilter", "label": "Pixel Filter", "type": "string", - "default": "box", + "default": "default", "page": "Pixel Filter", - "widget": "mapper", - "options": "Default:default|Box:box|Sinc:sinc|Gaussian:gaussian|Triangle:triangle|Catmull-Rom:catmull-rom", - "help": "", + "widget": "popup", + "options": "default|box|triangle|catmull-rom|sinc|gaussian|mitchell|separable-catmull-rom|blackman-harris|lanczos|bessel|disk|min|max|average|zmin|zmax|sum", + "help": "The name of the pixel filter to be used for the output display. In addition, five special filters may be used: min, max, average, zmin, and zmax. The first three filters have the same meaning as the depthfilter argument to Hider, i.e. instead of running a convolution filter across all samples, only a single value (the minimum, maximum, or average of all pixel samples) is returned and written into the final pixel value. The zmin and zmax filters operate like the min and max filters, except that the depth value of the pixel sample is used for comparison, and not the value implied by the mode itself. These filters are useful for arbitrary output variables where standard alpha compositing does not make sense, or where linear interpolation of values between disjoint pieces of geometry is nonsensical. Note that when these filters are used, opacity thresholding is also used on that output to determine which closest surface to sample.", "conditionalVisOps": { "conditionalVisOp": "equalTo", "conditionalVisPath": "show_advanced", @@ -138,7 +138,7 @@ "page": "Statistics", "widget": "mapper", "options": "None:none|Variance:variance|MSE:mse|Even:even|Odd:odd", - "help": "", + "help": "Default to empty. Indicates that this display channel should compute statistical measures on another display channel (specified via the source parameter). The statistics channel must have matching filter settings. The options available are: 'variance' to estimate the variance of the samples in each pixel, 'mse' which is the estimate of the variance divided by the actual number of samples per pixel, 'even' for an image created from half the total camera samples, and 'odd' for an image from the other half of the camera samples.", "conditionalVisOps": { "conditionalVisOp": "equalTo", "conditionalVisPath": "show_advanced", diff --git a/rman_ui/rman_ui_aovs.py b/rman_ui/rman_ui_aovs.py index ba8fe88f..ca1f1985 100644 --- a/rman_ui/rman_ui_aovs.py +++ b/rman_ui/rman_ui_aovs.py @@ -311,18 +311,18 @@ def draw_item(self, layout, context, item): col = layout.column() col.label(text="Remap Settings") row = col.row(align=True) - row.prop(channel, "remap_a", text="A") - row.prop(channel, "remap_b", text="B") - row.prop(channel, "remap_c", text="C") + row.prop(channel, "remap_a") + row.prop(channel, "remap_b") + row.prop(channel, "remap_c") layout.separator() - if rm.hider_pixelFilterMode != 'importance': - row = col.row() - row.prop(channel, "chan_pixelfilter") - row = col.row() - if channel.chan_pixelfilter != 'default': - row.prop(channel, "chan_pixelfilter_x", text="Size X") - row.prop(channel, "chan_pixelfilter_y", text="Size Y") - layout.separator() + row = col.row() + row.prop(channel, "chan_pixelfilter") + row = col.row() + if channel.chan_pixelfilter != 'default': + row.prop(channel, "chan_pixelfilter_x", text="Size X") + row.prop(channel, "chan_pixelfilter_y", text="Size Y") + layout.separator() + row = col.row() row.prop(channel, "stats_type") layout.separator() From a0ba4850e5d28c0066fa3327c466f58ed2273d22 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 2 May 2022 12:44:04 -0700 Subject: [PATCH 098/278] Fix typo when setting the beauty raw filepath. --- rman_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_render.py b/rman_render.py index b8b22138..8c8110eb 100644 --- a/rman_render.py +++ b/rman_render.py @@ -628,7 +628,7 @@ def start_render(self, depsgraph, for_background=False): # i.e.: it does not include the result of the Blender # compositor toks = os.path.splitext(filepath) - filepath = '%s_%s%s' % (toks[0], 'beauty_raw.exr') + filepath = '%s_%s' % (toks[0], 'beauty_raw.exr') bl_image = bpy.data.images.new(dspy_nm, width, height) try: From 99cecacf873d041f445b7d05b4b97f1d9329533e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 2 May 2022 13:16:09 -0700 Subject: [PATCH 099/278] Fix get_beauty_filepath function. Got the conditional that checked when we were using rman displays wrong. --- rfb_utils/display_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 0d2e285e..6589cc6c 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -28,11 +28,11 @@ def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False): if use_blender_frame: filePath = re.sub(r'<[f|F]\d*>', '####', filePath) if rm_rl: - file_format = bl_scene.render.image_settings.file_format - display_driver = __BLENDER_TO_RMAN_DSPY__.get(file_format, 'openexr') - else: aov = rm_rl.custom_aovs[0] display_driver = aov.displaydriver + else: + file_format = bl_scene.render.image_settings.file_format + display_driver = __BLENDER_TO_RMAN_DSPY__.get(file_format, 'openexr') if expand_tokens: filePath = string_utils.expand_string(filePath, From 5196ad4ba596c1a2477f5096c21ac5f4c1b2a320 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 2 May 2022 15:56:24 -0700 Subject: [PATCH 100/278] * add a force parameter to RfBStatsManager's attach method. This allows us to force the connection to the live stats manager, even if the user has disabled it in the prefs. * make sure to force the stats to connect in all preview render cases for XPU, not just when rendering to the Blender render window. --- rman_render.py | 12 ++++++------ rman_stats/__init__.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/rman_render.py b/rman_render.py index 8c8110eb..709d5206 100644 --- a/rman_render.py +++ b/rman_render.py @@ -438,6 +438,10 @@ def start_stats_thread(self): __RMAN_STATS_THREAD__.join() __RMAN_STATS_THREAD__ = None __RMAN_STATS_THREAD__ = threading.Thread(target=call_stats_update_payloads, args=(self, )) + if self.rman_is_xpu: + # FIXME: for now, add a 1 second delay before starting the stats thread + # for some reason, XPU doesn't seem to reset the progress between renders + time.sleep(1.0) __RMAN_STATS_THREAD__.start() def reset(self): @@ -498,12 +502,12 @@ def start_render(self, depsgraph, for_background=False): self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) was_connected = self.stats_mgr.is_connected() - if self.rman_is_xpu and self.rman_render_into == 'blender': + if self.rman_is_xpu: if not was_connected: # force the stats to start in the case of XPU # this is so that we can get a progress percentage # if we can't get it to start, abort - self.stats_mgr.attach() + self.stats_mgr.attach(force=True) time.sleep(0.5) # give it a second to attach if not self.stats_mgr.is_connected(): self.bl_engine.report({'ERROR'}, 'Cannot start live stats. Aborting XPU render') @@ -590,10 +594,6 @@ def start_render(self, depsgraph, for_background=False): render_pass = result.layers[0].passes.find_by_name(dspy_nm, render_view) bl_image_rps[i] = render_pass - if self.rman_is_xpu: - # FIXME: for now, add a 1 second delay before starting the stats thread - # for some reason, XPU doesn't seem to reset the progress between renders - time.sleep(1.0) self.start_stats_thread() while self.bl_engine and not self.bl_engine.test_break() and self.rman_is_live_rendering: time.sleep(0.01) diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index bba7724e..3f9b5804 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -188,11 +188,14 @@ def init_stats_session(self): self.update_session_config() self.rman_stats_session = rman.Stats.AddSession(self.rman_stats_session_config) - def update_session_config(self): + def update_session_config(self, force_enabled=False): self.web_socket_enabled = prefs_utils.get_pref('rman_roz_liveStatsEnabled', default=False) self.web_socket_port = prefs_utils.get_pref('rman_roz_webSocketServer_Port', default=0) + if force_enabled: + self.web_socket_enabled = True + config_dict = dict() config_dict["logLevel"] = int(prefs_utils.get_pref('rman_roz_logLevel', default='3')) config_dict["webSocketPort"] = self.web_socket_port @@ -242,10 +245,15 @@ def boot_strap(self): self.mgr.enableMetric(name) return - def attach(self): + def attach(self, force=False): if not self.mgr: return + + if force: + # force the live stats to be enabled + self.update_session_config(force_enabled=True) + if (self.mgr.clientConnected()): return From 12b81bf00d3149834ae4fcc85ed07f42fc21a6fc Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 3 May 2022 12:56:08 -0700 Subject: [PATCH 101/278] Add use_bl_compositor and use_bl_compositor_write_aovs settings. These only apply to blender batch mode. * with use_bl_compositor on, we write out the composited image from Blender's compositor. * when use_bl_compositor_write_aovs is also on, the AOV's ane the beauty are written out as exr files. --- rfb_utils/scene_utils.py | 20 ++++++ rman_config/config/rman_properties_scene.json | 43 ++++++++++++- rman_handlers/__init__.py | 10 +-- rman_render.py | 61 ++++++++++--------- 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index 982f6d2c..2fc87f99 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -45,6 +45,26 @@ def get_renderman_layer(context): return rm_rl +def should_use_bl_compositor(bl_scene): + ''' + Check if we should use the Blender compositor + + Args: + bl_scene (bpy.types.Scene) - the Blender scene + + Returns: + (bool) - true if we should use the compositor; false if not + ''' + if not bpy.app.background: + return True + + rm = bl_scene.renderman + if not rm.use_bl_compositor: + # explicitiy turned off + return False + + return bl_scene.use_nodes + def any_areas_shading(): ''' Loop through all of the windows/areas and return True if any of diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 8271439a..82f265db 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1294,6 +1294,42 @@ "options": "LocalQueue:lq|Tractor:tractor|None:none", "help": "System to spool to. None will generate RIB files, but not spool a render." }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "", + "name": "use_bl_compositor", + "label": "Use Blender Compositor", + "type": "int", + "default": 1, + "widget": "checkbox", + "help": "Only applies to Blender Batch. When enabled, all of the RenderMan Display settings are ignored, and one final image from the Blender compositor is written out. When using RIB, the Blender compositor cannot be used.", + "conditionalVisOps": { + "conditionalVisOp": "equalTo", + "conditionalVisPath": "spool_style", + "conditionalVisValue": "batch" + } + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "", + "name": "use_bl_compositor_write_aovs", + "label": "Blender Batch AOVs", + "type": "int", + "default": 0, + "widget": "checkbox", + "help": "Only applies to Blender Batch. If on, and Use Blender Compositor is also turned on, all AOVs will be written out as OpenEXR files.", + "conditionalVisOps": { + "conditionalVis1Path": "spool_style", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "batch", + "conditionalVisOp": "and", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "use_bl_compositor", + "conditionalVis2Value": "1" + } + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "", @@ -1302,7 +1338,12 @@ "type": "int", "default": 0, "widget": "checkbox", - "help": "Attempt to resume render from a previous checkpoint (if possible)." + "help": "Attempt to resume render from a previous checkpoint (if possible). This is only avaliable for RIB renders.", + "conditionalVisOps": { + "conditionalVisOp": "equalTo", + "conditionalVisPath": "spool_style", + "conditionalVisValue": "rib" + } }, { "panel": "RENDER_PT_renderman_spooling_export_options", diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index fd0d99c9..5f650154 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -45,21 +45,23 @@ def render_pre(bl_scene): ''' render_pre handler that changes the Blender filepath attribute to match our filename output format. In the case of background - mode, set it to a temporary filename. The temporary filename will - get removed in the render_post handler. + mode, and use_bl_compositor is off, set it to a temporary filename. + The temporary filename will get removed in the render_post handler. ''' global __ORIGINAL_BL_FILEPATH__ global __ORIGINAL_BL_FILE_FORMAT__ global __BL_TMP_FILE__ global __BL_TMP_DIR__ from ..rfb_utils import display_utils + from ..rfb_utils import scene_utils __ORIGINAL_BL_FILEPATH__ = bl_scene.render.filepath __ORIGINAL_BL_FILE_FORMAT__ = bl_scene.render.image_settings.file_format - if not bpy.app.background: + write_comp = scene_utils.should_use_bl_compositor(bl_scene) + if write_comp: filePath = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True) bl_scene.render.filepath = filePath - bl_scene.render.image_settings.file_format = 'OPEN_EXR' + bl_scene.render.image_settings.file_format = 'OPEN_EXR' else: __BL_TMP_FILE__ = os.path.join(__BL_TMP_DIR__, '####.png') bl_scene.render.filepath = __BL_TMP_FILE__ diff --git a/rman_render.py b/rman_render.py index 709d5206..6064754f 100644 --- a/rman_render.py +++ b/rman_render.py @@ -464,9 +464,13 @@ def start_render(self, depsgraph, for_background=False): if not self._check_prman_license(): return False + use_compositor = scene_utils.should_use_bl_compositor(self.bl_scene) if for_background: self.rman_render_into = '' is_external = True + if use_compositor: + self.rman_render_into = 'blender' + is_external = False self.rman_callbacks.clear() ec = rman.EventCallbacks.Get() ec.RegisterCallback("Render", render_cb, self) @@ -614,36 +618,33 @@ def start_render(self, depsgraph, for_background=False): if self.bl_engine: self.bl_engine.end_result(result) - # Try to save out the displays out to disk. This matches - # Cycles behavior - for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): - filepath = dspy_dict['displays'][dspy_nm]['filePath'] - buffer = self._get_buffer(width, height, image_num=i, as_flat=True) - if buffer is None: - continue - - if i == 0: - # if this is the beauty, add the substring 'beauty_raw' - # to indicate this is the "raw" beauty - # i.e.: it does not include the result of the Blender - # compositor - toks = os.path.splitext(filepath) - filepath = '%s_%s' % (toks[0], 'beauty_raw.exr') - - bl_image = bpy.data.images.new(dspy_nm, width, height) - try: - if isinstance(buffer, numpy.ndarray): - buffer = buffer.tolist() - bl_image.use_generated_float = True - bl_image.filepath_raw = filepath - bl_image.pixels.foreach_set(buffer) - bl_image.file_format = 'OPEN_EXR' - bl_image.update() - bl_image.save() - except: - pass - finally: - bpy.data.images.remove(bl_image) + # check if we should write out the AOVs + if use_compositor and rm.use_bl_compositor_write_aovs: + for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): + filepath = dspy_dict['displays'][dspy_nm]['filePath'] + buffer = self._get_buffer(width, height, image_num=i, as_flat=True) + if buffer is None: + continue + + if i == 0: + # write out the beauty with a 'raw' substring + toks = os.path.splitext(filepath) + filepath = '%s_beauty_raw.exr' % (toks[0]) + + bl_image = bpy.data.images.new(dspy_nm, width, height) + try: + if isinstance(buffer, numpy.ndarray): + buffer = buffer.tolist() + bl_image.use_generated_float = True + bl_image.filepath_raw = filepath + bl_image.pixels.foreach_set(buffer) + bl_image.file_format = 'OPEN_EXR' + bl_image.update() + bl_image.save() + except: + pass + finally: + bpy.data.images.remove(bl_image) if not was_connected and self.stats_mgr.is_connected(): # if stats were not started before rendering, disconnect From 0f1d392445d4bc7eac1d94ae60a68034c97fc81f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 3 May 2022 14:03:59 -0700 Subject: [PATCH 102/278] Make sure we don't generate denoise tasks if we are using the Blender compositor. --- rman_spool.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rman_spool.py b/rman_spool.py index b0de7bba..8974fdd2 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -367,8 +367,9 @@ def blender_batch_render(self, bl_filename): job.addChild(parent_task) - # Don't denoise if we're baking - if rm.hider_type == 'RAYTRACE': + # Don't generate denoise tasks if we're baking + # or using the Blender compositor + if rm.hider_type == 'RAYTRACE' and (not rm.use_bl_compositor or not scene.use_nodes): parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) job.addChild(parent_task) From 6108bc0c10d24603e6d66622d0e35cd3a042c472 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 4 May 2022 13:00:30 -0700 Subject: [PATCH 103/278] Add support for Blender's use_persistent_data. For animated renders, this allows us to get the diffs in between frames, rather than having to re-export the whole scene. Currently this only works with RIB batch renders. --- rman_config/config/rman_properties_scene.json | 16 +- rman_render.py | 94 +++++-- rman_scene.py | 4 +- rman_scene_sync.py | 230 ++++++++++++++++++ 4 files changed, 316 insertions(+), 28 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 82f265db..65ed4d2b 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -144,8 +144,20 @@ "widget": "mapper", "options": "Off:OFF|In Alpha:ALPHA|Separate AOV:AOV", "help": "Render a holdout matte." - - }, + }, + { + "panel": "RENDER_PT_renderman_render", + "page": "", + "name": "do_persistent_data", + "label": "Persistent Data", + "type": "int", + "default": 0, + "widget": "checkbox", + "help": "Keep render data around for faster animation renders, at the cost of increased memory usage. May not work in all cases.", + "editable": false, + "update_function_name": "update_do_persistent_data", + "update_function": "def update_do_persistent_data(self, context):\n scene = context.scene\n scene.render.use_persistent_data = self.do_persistent_data" + }, { "panel": "RENDER_PT_renderman_sampling", "page": "IPR Sampling", diff --git a/rman_render.py b/rman_render.py index 6064754f..63d473eb 100644 --- a/rman_render.py +++ b/rman_render.py @@ -678,34 +678,78 @@ def start_external_render(self, depsgraph): if rm.external_animation: original_frame = bl_scene.frame_current - rfb_log().debug("Writing to RIB...") - for frame in range(bl_scene.frame_start, bl_scene.frame_end + 1): - bl_view_layer = depsgraph.view_layer - config = rman.Types.RtParamList() - render_config = rman.Types.RtParamList() + do_persistent_data = rm.do_persistent_data + rfb_log().debug("Writing to RIB...") + time_start = time.time() - self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) - try: - self.bl_engine.frame_set(frame, subframe=0.0) - rfb_log().debug("Frame: %d" % frame) - self.rman_is_exporting = True - self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) - self.rman_is_exporting = False - rib_output = string_utils.expand_string(rm.path_rib_output, - frame=frame, - asFilePath=True) - self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) + if do_persistent_data: + + for frame in range(bl_scene.frame_start, bl_scene.frame_end + 1): + bl_view_layer = depsgraph.view_layer + config = rman.Types.RtParamList() + render_config = rman.Types.RtParamList() + + if self.sg_scene is None: + self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) + try: + self.bl_engine.frame_set(frame, subframe=0.0) + rfb_log().debug("Frame: %d" % frame) + if frame == bl_scene.frame_start: + self.rman_is_exporting = True + self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) + self.rman_is_exporting = False + else: + self.rman_scene_sync.batch_update_scene(bpy.context, depsgraph) + + rib_output = string_utils.expand_string(rm.path_rib_output, + frame=frame, + asFilePath=True) + self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) + + except Exception as e: + self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().error('Export Failed:\n%s' % traceback.format_exc()) + self.stop_render(stop_draw_thread=False) + self.del_bl_engine() + return False + + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() + else: + for frame in range(bl_scene.frame_start, bl_scene.frame_end + 1): + bl_view_layer = depsgraph.view_layer + config = rman.Types.RtParamList() + render_config = rman.Types.RtParamList() + + self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) + try: + self.bl_engine.frame_set(frame, subframe=0.0) + rfb_log().debug("Frame: %d" % frame) + self.rman_is_exporting = True + self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) + self.rman_is_exporting = False + + rib_output = string_utils.expand_string(rm.path_rib_output, + frame=frame, + asFilePath=True) + self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) + self.sgmngr.DeleteScene(self.sg_scene) + self.sg_scene = None + self.rman_scene.reset() + + except Exception as e: + self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) + rfb_log().error('Export Failed:\n%s' % traceback.format_exc()) + self.stop_render(stop_draw_thread=False) + self.del_bl_engine() + return False + + if self.sg_scene: self.sgmngr.DeleteScene(self.sg_scene) self.sg_scene = None - self.rman_scene.reset() - - except Exception as e: - self.bl_engine.report({'ERROR'}, 'Export failed: %s' % str(e)) - rfb_log().error('Export Failed:\n%s' % traceback.format_exc()) - self.stop_render(stop_draw_thread=False) - self.del_bl_engine() - return False - + self.rman_scene.reset() + rfb_log().info("Finished parsing scene. Total time: %s" % string_utils._format_time_(time.time() - time_start)) self.bl_engine.frame_set(original_frame, subframe=0.0) diff --git a/rman_scene.py b/rman_scene.py index bdefd9ff..55550a22 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -794,7 +794,9 @@ def export_instances_motion(self, selected_objects=False): motion_steps = sorted(list(self.motion_steps)) first_sample = False - delta = -motion_steps[0] + delta = 0.0 + if len(motion_steps) > 0: + delta = -motion_steps[0] psys_translator = self.rman_translators['PARTICLES'] rman_group_translator = self.rman_translators['GROUP'] for samp, seg in enumerate(motion_steps): diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 73789c6d..129b32b9 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -336,6 +336,236 @@ def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): portal.original.update_tag() + def batch_update_scene(self, context, depsgraph): + self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current + + self.rman_updates = dict() + self.num_instances_changed = False + self.check_all_instances = False + + self.rman_scene.bl_scene = depsgraph.scene_eval + self.rman_scene.context = context + + options = self.rman_scene.sg_scene.GetOptions() + options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.rman_scene.bl_frame_current) + self.rman_scene.sg_scene.SetOptions(options) + + # Check the number of instances. If we differ, an object may have been + # added or deleted + if self.rman_scene.num_object_instances != len(depsgraph.object_instances): + rfb_log().debug("\tNumber of instances changed: %d -> %d" % (self.rman_scene.num_object_instances, len(depsgraph.object_instances))) + self.num_instances_changed = True + self.rman_scene.num_object_instances = len(depsgraph.object_instances) + + for obj in reversed(depsgraph.updates): + ob = obj.id + + if isinstance(obj.id, bpy.types.Camera): + rfb_log().debug("Camera updated: %s" % obj.id.name) + if self.rman_scene.is_viewport_render: + if self.rman_scene.bl_scene.camera.data != obj.id: + continue + rman_sg_camera = self.rman_scene.main_camera + translator = self.rman_scene.rman_translators['CAMERA'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + translator.update_viewport_cam(self.rman_scene.bl_scene.camera, rman_sg_camera, force_update=True) + else: + translator = self.rman_scene.rman_translators['CAMERA'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + for ob, rman_sg_camera in self.rman_scene.rman_cameras.items(): + if ob.original.name != obj.id.name: + continue + translator._update_render_cam(ob.original, rman_sg_camera) + + elif isinstance(obj.id, bpy.types.ParticleSettings): + rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) + for o in self.rman_scene.bl_scene.objects: + psys = None + ob = o.evaluated_get(depsgraph) + for ps in ob.particle_systems: + if ps.settings.original == obj.id.original: + psys = ps + break + if not psys: + continue + if object_utils.is_particle_instancer(psys): + #self.check_particle_instancer(obj, psys) + pass + else: + self.update_particle_emitter(ob, psys) + + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[o.original] = rman_update + + elif isinstance(obj.id, bpy.types.Object): + ob_eval = obj.id.evaluated_get(depsgraph) + rman_type = object_utils._detect_primitive_(ob_eval) + + if ob.type in ('ARMATURE'): + continue + + # These types need special handling + if rman_type == 'EMPTY': + self.update_empty(obj) + continue + if rman_type == 'LIGHTFILTER': + rfb_log().debug("Light filter: %s updated" % obj.id.name) + ob_eval = obj.id.evaluated_get(self.rman_scene.depsgraph) + proto_key = object_utils.prototype_key(ob_eval) + rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) + if not rman_sg_lightfilter: + continue + if obj.is_updated_geometry: + self.rman_scene.rman_translators['LIGHTFILTER'].update(ob_eval, rman_sg_lightfilter) + for light_ob in rman_sg_lightfilter.lights_list: + if isinstance(light_ob, bpy.types.Material): + ob_key = light_ob.original + else: + ob_key = object_utils.prototype_key(light_ob) + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + if obj.is_updated_transform: + rman_group_translator = self.rman_scene.rman_translators['GROUP'] + rman_group_translator.update_transform(ob_eval, rman_sg_lightfilter) + + if rman_type == 'CAMERA': + rfb_log().debug("Camera: %s updated" % obj.id.name) + cam_translator = self.rman_scene.rman_translators['CAMERA'] + rman_sg_camera = self.rman_scene.rman_cameras.get(ob_eval.original) + if obj.is_updated_geometry: + cam_translator.update(ob_eval, rman_sg_camera) + if obj.is_updated_transform: + cam_translator._update_render_cam_transform(ob_eval, rman_sg_camera) + continue + + rman_update = RmanUpdate() + + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + + rfb_log().debug("\tObject: %s Updated" % obj.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + self.rman_updates[obj.id.original] = rman_update + + if not self.rman_updates and self.num_instances_changed: + # The number of instances changed, but we are not able + # to determine what changed. We are forced to check + # all instances. + rfb_log().debug("Set check_all_instances to True") + self.check_all_instances = True + + if self.check_all_instances or self.rman_updates: + self.check_instances_batch() + + # update any materials + material_translator = self.rman_scene.rman_translators['MATERIAL'] + for id, rman_sg_material in self.rman_scene.rman_materials.items(): + if rman_sg_material.is_frame_sensitive or id.original in self.rman_updates: + mat = id.original + material_translator.update(mat, rman_sg_material) + + self.rman_scene.export_integrator() + self.rman_scene.export_samplefilters() + self.rman_scene.export_displayfilters() + self.rman_scene.export_displays() + + if self.rman_scene.do_motion_blur and self.rman_scene.moving_objects: + self.rman_scene.export_instances_motion() + + @time_this + def check_instances_batch(self): + already_udpated = list() # list of objects already updated during our loop + clear_instances = list() # list of objects who had their instances cleared + rfb_log().debug("Updating instances") + + for instance in self.rman_scene.depsgraph.object_instances: + if instance.object.type in ('ARMATURE', 'CAMERA'): + continue + ob_key = instance.object.original + ob_eval = instance.object.evaluated_get(self.rman_scene.depsgraph) + instance_parent = None + psys = None + proto_key = object_utils.prototype_key(instance) + if instance.is_instance: + ob_key = instance.instance_object.original + psys = instance.particle_system + instance_parent = instance.parent + + if self.rman_scene.do_motion_blur: + if ob_key.name_full in self.rman_scene.moving_objects: + continue + + rman_type = object_utils._detect_primitive_(ob_eval) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key, ob=ob_eval) + if not rman_sg_node: + continue + + if self.check_all_instances: + # check all instances in the scene + rman_update = self.rman_updates.get(ob_key, None) + if not rman_update: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + else: + if ob_key not in self.rman_updates: + if rman_sg_node.is_frame_sensitive: + rman_update = RmanUpdate() + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True + self.rman_updates[ob_key] = rman_update + else: + if not instance_parent: + continue + if instance_parent.original not in self.rman_updates: + continue + rman_update = self.rman_updates[instance_parent.original] + else: + rman_update = self.rman_updates[ob_key] + + if rman_sg_node and not instance.is_instance: + if rman_update.is_updated_geometry and proto_key not in already_udpated: + translator = self.rman_scene.rman_translators.get(rman_type, None) + rfb_log().debug("\tUpdating Object: %s" % proto_key) + translator.update(ob_eval, rman_sg_node) + self.update_particle_emitters(ob_eval) + already_udpated.append(proto_key) + + if rman_type == 'EMPTY': + self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) + + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue + + # clear all instances for this prototype, if + # we have not already done so + if instance_parent: + parent_proto_key = object_utils.prototype_key(instance_parent) + rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) + if rman_parent_node and rman_parent_node not in clear_instances: + rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) + rman_parent_node.clear_instances() + clear_instances.append(rman_parent_node) + elif rman_sg_node not in clear_instances: + rfb_log().debug("\tClearing instances: %s" % proto_key) + rman_sg_node.clear_instances() + clear_instances.append(rman_sg_node) + + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) + @time_this def update_scene(self, context, depsgraph): ## FIXME: this function is waaayyy too big and is doing too much stuff From 246a17217269e10f22c00bdd59e54b5f813b89eb Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 4 May 2022 14:35:24 -0700 Subject: [PATCH 104/278] * move check_frame_sensitive into string_utils * make sure our file based primitives are marked frame sensitive correctly * remove a couple of old attributes from the RIB archive panel --- rfb_utils/property_utils.py | 13 +-------- rfb_utils/string_utils.py | 15 +++++++++++ .../rman_properties_misc/__init__.py | 19 ------------- .../rman_properties_object/__init__.py | 7 +---- rman_scene_sync.py | 27 +++++-------------- rman_translators/rman_alembic_translator.py | 4 ++- rman_translators/rman_brickmap_translator.py | 4 +++ rman_translators/rman_dra_translator.py | 4 +++ rman_translators/rman_openvdb_translator.py | 7 ++--- rman_ui/rman_ui_object_panels.py | 9 +------ 10 files changed, 40 insertions(+), 69 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 19f82d5d..8b652f75 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -480,17 +480,6 @@ def vstruct_conditional(node, param): new_tokens.extend(['else', 'False']) return eval(" ".join(new_tokens)) -def check_frame_sensitive(prop): - # check if the prop value has any frame token - # ex: , , etc. - # if it does, it means we need to issue a material - # update if the frame changes - pat = re.compile(r'<[f|F]\d*>') - m = pat.search(prop) - if m: - return True - return False - def set_dspymeta_params(node, prop_name, params): if node.plugin_name not in ['openexr', 'deepexr']: # for now, we only accept openexr an deepexr @@ -796,7 +785,7 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group elif param_type == 'string': if not is_frame_sensitive: - is_frame_sensitive = check_frame_sensitive(prop) + is_frame_sensitive = string_utils.check_frame_sensitive(prop) val = string_utils.expand_string(prop) options = meta['options'] diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 8411bd09..55b13b8b 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -164,6 +164,10 @@ def get_var(nm): converter_validity_check() return __SCENE_STRING_CONVERTER__.get_token(nm) +def update_frame_token(frame): + converter_validity_check() + __SCENE_STRING_CONVERTER__.expr.set_frame_context(frame) + def get_tokenized_openvdb_file(frame_filepath, grids_frame): openvdb_file = filepath_utils.get_real_path(frame_filepath) f = os.path.basename(frame_filepath) @@ -213,6 +217,17 @@ def update_blender_tokens_cb(bl_scene): __SCENE_STRING_CONVERTER__.update(bl_scene=scene) +def check_frame_sensitive(s): + # check if the sting has any frame token + # ex: , , etc. + # if it does, it means we need to issue a material + # update if the frame changes + pat = re.compile(r'<[f|F]\d*>') + m = pat.search(s) + if m: + return True + return False + def _format_time_(seconds): hours = seconds // (60 * 60) seconds %= (60 * 60) diff --git a/rman_properties/rman_properties_misc/__init__.py b/rman_properties/rman_properties_misc/__init__.py index 6c02f7a4..5a5ed249 100644 --- a/rman_properties/rman_properties_misc/__init__.py +++ b/rman_properties/rman_properties_misc/__init__.py @@ -249,24 +249,6 @@ class RendermanArrayGroup(bpy.types.PropertyGroup): value_normal: FloatVectorProperty(name="Value", default=(0.0,0.0,0.0), size=3, subtype="NONE") value_point: FloatVectorProperty(name="Value", default=(0.0,0.0,0.0), size=3, subtype="XYZ") -class RendermanAnimSequenceSettings(bpy.types.PropertyGroup): - animated_sequence: BoolProperty( - name="Animated Sequence", - description="Interpret this archive as an animated sequence (converts #### in file path to frame number)", - default=False) - sequence_in: IntProperty( - name="Sequence In Point", - description="The first numbered file to use", - default=1) - sequence_out: IntProperty( - name="Sequence Out Point", - description="The last numbered file to use", - default=24) - blender_start: IntProperty( - name="Blender Start Frame", - description="The frame in Blender to begin playing back the sequence", - default=1) - class Tab_CollectionGroup(bpy.types.PropertyGroup): ################# @@ -325,7 +307,6 @@ class Tab_CollectionGroup(bpy.types.PropertyGroup): LightLinking, RendermanMeshPrimVar, RendermanReferencePosePrimVars, - RendermanAnimSequenceSettings, Tab_CollectionGroup, RENDERMAN_UL_Array_List, RendermanArrayGroup diff --git a/rman_properties/rman_properties_object/__init__.py b/rman_properties/rman_properties_object/__init__.py index bac85466..55620952 100644 --- a/rman_properties/rman_properties_object/__init__.py +++ b/rman_properties/rman_properties_object/__init__.py @@ -4,7 +4,6 @@ from ... import rman_config from ...rman_config import RmanBasePropertyGroup -from ..rman_properties_misc import RendermanAnimSequenceSettings from ..rman_properties_misc import RendermanLightPointer from ...rfb_utils import shadergraph_utils from ...rfb_utils import object_utils @@ -43,11 +42,7 @@ class RendermanObjectSettings(RmanBasePropertyGroup, bpy.types.PropertyGroup): rman_config_name: StringProperty(name='rman_config_name', default='rman_properties_object') - - archive_anim_settings: PointerProperty( - type=RendermanAnimSequenceSettings, - name="Animation Sequence Settings") - + hide_primitive_type: BoolProperty( name="Hide Primitive Type", default=False diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 129b32b9..cdd302be 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -3,6 +3,7 @@ from .rfb_utils import texture_utils from .rfb_utils import scene_utils from .rfb_utils.timer_utils import time_this +from .rfb_utils import string_utils from .rfb_logger import rfb_log from .rman_sg_nodes.rman_sg_lightfilter import RmanSgLightFilter @@ -99,6 +100,7 @@ def scene_updated(self): # are marked as frame sensitive rfb_log().debug("Frame changed: %d -> %d" % (self.rman_scene.bl_frame_current, self.rman_scene.bl_scene.frame_current)) self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current + string_utils.update_frame_token(self.rman_scene.bl_frame_current) self.frame_number_changed = True # check for frame sensitive objects @@ -338,6 +340,7 @@ def update_portals(self, ob): def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current + string_utils.update_frame_token(self.rman_scene.bl_frame_current) self.rman_updates = dict() self.num_instances_changed = False @@ -360,24 +363,7 @@ def batch_update_scene(self, context, depsgraph): for obj in reversed(depsgraph.updates): ob = obj.id - if isinstance(obj.id, bpy.types.Camera): - rfb_log().debug("Camera updated: %s" % obj.id.name) - if self.rman_scene.is_viewport_render: - if self.rman_scene.bl_scene.camera.data != obj.id: - continue - rman_sg_camera = self.rman_scene.main_camera - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - translator.update_viewport_cam(self.rman_scene.bl_scene.camera, rman_sg_camera, force_update=True) - else: - translator = self.rman_scene.rman_translators['CAMERA'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for ob, rman_sg_camera in self.rman_scene.rman_cameras.items(): - if ob.original.name != obj.id.name: - continue - translator._update_render_cam(ob.original, rman_sg_camera) - - elif isinstance(obj.id, bpy.types.ParticleSettings): + if isinstance(obj.id, bpy.types.ParticleSettings): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) for o in self.rman_scene.bl_scene.objects: psys = None @@ -466,8 +452,7 @@ def batch_update_scene(self, context, depsgraph): rfb_log().debug("Set check_all_instances to True") self.check_all_instances = True - if self.check_all_instances or self.rman_updates: - self.check_instances_batch() + self.check_instances_batch() # update any materials material_translator = self.rman_scene.rman_translators['MATERIAL'] @@ -524,6 +509,7 @@ def check_instances_batch(self): if ob_key not in self.rman_updates: if rman_sg_node.is_frame_sensitive: rman_update = RmanUpdate() + rman_update.is_updated_geometry = True rman_update.is_updated_shading = True rman_update.is_updated_transform = True self.rman_updates[ob_key] = rman_update @@ -809,6 +795,7 @@ def check_instances(self): rman_update = self.rman_updates.get(ob_key, None) if not rman_update: rman_update = RmanUpdate() + rman_update.is_updated_geometry = True rman_update.is_updated_shading = True rman_update.is_updated_transform = True self.rman_updates[ob_key] = rman_update diff --git a/rman_translators/rman_alembic_translator.py b/rman_translators/rman_alembic_translator.py index 51819d79..b3295e33 100644 --- a/rman_translators/rman_alembic_translator.py +++ b/rman_translators/rman_alembic_translator.py @@ -35,7 +35,9 @@ def update(self, ob, rman_sg_alembic): abc_frame = rm.abc_frame if rm.abc_use_scene_frame: rman_sg_alembic.is_frame_sensitive = True - abc_frame = float(self.rman_scene.bl_frame_current) + abc_frame = float(self.rman_scene.bl_frame_current) + elif string_utils.check_frame_sensitive(abc_filepath): + rman_sg_alembic.is_frame_sensitive = True else: rman_sg_alembic.is_frame_sensitive = False diff --git a/rman_translators/rman_brickmap_translator.py b/rman_translators/rman_brickmap_translator.py index 410d61e4..7be5cb04 100644 --- a/rman_translators/rman_brickmap_translator.py +++ b/rman_translators/rman_brickmap_translator.py @@ -24,6 +24,10 @@ def export_deform_sample(self, rman_sg_brickmap, ob, time_sample): def update(self, ob, rman_sg_brickmap): primvar = rman_sg_brickmap.sg_node.GetPrimVars() rm = ob.renderman + if string_utils.check_frame_sensitive(rm.bkm_filepath): + rman_sg_brickmap.is_frame_sensitive = True + else: + rman_sg_brickmap.is_frame_sensitive = False bkm_filepath = string_utils.expand_string(rm.bkm_filepath) primvar.SetString("filename", bkm_filepath) super().export_object_primvars(ob, primvar) diff --git a/rman_translators/rman_dra_translator.py b/rman_translators/rman_dra_translator.py index 15be3d4b..1fade466 100644 --- a/rman_translators/rman_dra_translator.py +++ b/rman_translators/rman_dra_translator.py @@ -22,6 +22,10 @@ def export_deform_sample(self, rman_sg_dra, ob, time_sample): def update(self, ob, rman_sg_dra): rm = ob.renderman + if string_utils.check_frame_sensitive(rm.path_archive): + rman_sg_dra.is_frame_sensitive = True + else: + rman_sg_dra.is_frame_sensitive = False path_archive = string_utils.expand_string(rm.path_archive) bounds = (-100000, 100000, -100000, 100000, -100000, 100000 ) diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index 4ddd1b7b..abc2a61d 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -57,9 +57,10 @@ def update(self, ob, rman_sg_openvdb): openvdb_file = filepath_utils.get_real_path(db.filepath) if db.is_sequence: # if we have a sequence, get the current frame filepath from the grids - openvdb_file = filepath_utils.get_real_path(grids.frame_filepath) - - #openvdb_file = texture_utils.get_txmanager().get_output_vdb(ob) + openvdb_file = filepath_utils.get_real_path(grids.frame_filepath) + rman_sg_openvdb.is_frame_sensitive = True + else: + rman_sg_openvdb.is_frame_sensitive = False openvdb_attrs = dict() openvdb_attrs['filterWidth'] = getattr(rm, 'openvdb_filterwidth') diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index d25ff116..74ffac8c 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -515,14 +515,7 @@ def draw(self, context): col = layout.column() col.enabled = not rman_interactive_running col = layout.column(align = True) - _draw_ui_from_rman_config('rman_properties_object', 'OBJECT_PT_renderman_object_geometry_rib_archive', context, layout, rm) - col.prop(anim, "animated_sequence") - if anim.animated_sequence: - col = layout.column(align = True) - col.prop(anim, "blender_start") - col.prop(anim, "sequence_in") - col.prop(anim, "sequence_out") - + _draw_ui_from_rman_config('rman_properties_object', 'OBJECT_PT_renderman_object_geometry_rib_archive', context, layout, rm) class OBJECT_PT_renderman_object_geometry_points(Panel, CollectionPanel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' From 07a0f6201643a35786f2d78c2e34cbb17e410095 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 4 May 2022 15:45:15 -0700 Subject: [PATCH 105/278] Add a frame chunking setting for blender batch renders. --- rman_config/config/rman_properties_scene.json | 20 ++++++++- rman_spool.py | 42 +++++++++++++------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 65ed4d2b..f6b58df9 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1305,7 +1305,23 @@ "widget": "mapper", "options": "LocalQueue:lq|Tractor:tractor|None:none", "help": "System to spool to. None will generate RIB files, but not spool a render." - }, + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "", + "name": "bl_batch_frame_chunk", + "label": "Frame Chunking", + "type": "int", + "default": 1, + "min": 1, + "max": 10, + "help": "Frame chunking. Determines how many frames each Blender batch task will render.", + "conditionalVisOps": { + "conditionalVisOp": "equalTo", + "conditionalVisPath": "spool_style", + "conditionalVisValue": "batch" + } + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "", @@ -1341,7 +1357,7 @@ "conditionalVis2Path": "use_bl_compositor", "conditionalVis2Value": "1" } - }, + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "", diff --git a/rman_spool.py b/rman_spool.py index 8974fdd2..5634cb57 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -82,7 +82,7 @@ def add_prman_render_task(self, parentTask, title, threads, rib, img): task.addCommand(command) parentTask.addChild(task) - def add_blender_render_task(self, frame, parentTask, title, bl_filename, img): + def add_blender_render_task(self, frame, parentTask, title, bl_filename, img, chunk=None): rm = self.bl_scene.renderman task = author.Task() @@ -97,16 +97,19 @@ def add_blender_render_task(self, frame, parentTask, title, bl_filename, img): command.argv.append('-b') command.argv.append('%%D(%s)' % bl_filename) - command.argv.append('-f') - command.argv.append(str(frame)) + if chunk: + command.argv.append('-f') + command.argv.append('%s..%s' % (str(frame), str(frame+(chunk)))) + else: + command.argv.append('-f') + command.argv.append(str(frame)) task.addCommand(command) parentTask.addChild(task) def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, - start, last, by, bl_filename): + start, last, by, chunk, bl_filename): - rm = self.bl_scene.renderman self.any_denoise = display_utils.any_dspys_denoise(self.rman_scene.bl_view_layer) img = None if not self.any_denoise: @@ -137,13 +140,25 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, (str(self.depsgraph.view_layer.name))) renderframestask.title = renderframestasktitle - for iframe in range(int(start), int(last + 1), int(by)): - - prmantasktitle = ("%s Frame: %d (blender)" % - (tasktitle, int(iframe))) - - self.add_blender_render_task(iframe, renderframestask, prmantasktitle, - bl_filename, img) + if chunk > 1 and by < 2 and (start+chunk < last+1): + iframe = start + while (iframe < last+1): + diff = chunk + if ((iframe + chunk) > last+1): + diff = (last) - iframe + prmantasktitle = ("%s Frames: (%d-%d) (blender)" % + (tasktitle, iframe, iframe+diff)) + + self.add_blender_render_task(iframe, renderframestask, prmantasktitle, + bl_filename, None, chunk=diff) + iframe += diff+1 + else: + for iframe in range(start, last + 1, by): + prmantasktitle = ("%s Frame: %d (blender)" % + (tasktitle, int(iframe))) + + self.add_blender_render_task(iframe, renderframestask, prmantasktitle, + bl_filename, img) parent_task.addChild(renderframestask) @@ -326,6 +341,7 @@ def blender_batch_render(self, bl_filename): frame_begin = self.bl_scene.frame_start frame_end = self.bl_scene.frame_end by = self.bl_scene.frame_step + chunk = rm.bl_batch_frame_chunk-1 # update variables string_utils.set_var('scene', self.bl_scene.name) @@ -363,7 +379,7 @@ def blender_batch_render(self, bl_filename): anim = (frame_begin != frame_end) self.generate_blender_batch_tasks(anim, parent_task, tasktitle, - frame_begin, frame_end, by, bl_filename) + frame_begin, frame_end, by, chunk, bl_filename) job.addChild(parent_task) From 213fabf5adf60a6b9f8136182587234bab8b4210 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 4 May 2022 15:54:22 -0700 Subject: [PATCH 106/278] Modify should_use_bl_compositor to also check that the use_compositing attribute is turned on. --- rfb_utils/scene_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index 2fc87f99..f843591e 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -63,7 +63,7 @@ def should_use_bl_compositor(bl_scene): # explicitiy turned off return False - return bl_scene.use_nodes + return bl_scene.use_nodes and bl_scene.render.use_compositing def any_areas_shading(): ''' From f44f79ecc7c180cc24f84e40eabec62dcbb4876e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 5 May 2022 08:41:02 -0700 Subject: [PATCH 107/278] Fix issues with lights and light filters not updating when parameters were key framed. * we need to check for changes to bpy.types.Light types, and then have each instance be updated. --- rman_scene_sync.py | 149 ++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index cdd302be..713b19c0 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -152,7 +152,10 @@ def _mesh_light_update(self, mat): ob.update_tag() def material_updated(self, obj): - mat = obj.id + if isinstance(obj, bpy.types.DepsgraphUpdate): + mat = obj.id + else: + mat = obj rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) translator = self.rman_scene.rman_translators["MATERIAL"] db_name = object_utils.get_db_name(mat) @@ -174,20 +177,19 @@ def material_updated(self, obj): # update db_name rman_sg_material.db_name = db_name - def light_filter_transform_updated(self, obj): - ob = obj.id.evaluated_get(self.rman_scene.depsgraph) - proto_key = object_utils.prototype_key(ob) - rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) - if rman_sg_lightfilter: - rman_group_translator = self.rman_scene.rman_translators['GROUP'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - rman_group_translator.update_transform(ob, rman_sg_lightfilter) + def light_filter_transform_updated(self, ob, rman_sg_lightfilter): + rman_group_translator = self.rman_scene.rman_translators['GROUP'] + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + rman_group_translator.update_transform(ob, rman_sg_lightfilter) - def light_filter_updated(self, obj): - ob = obj.id.evaluated_get(self.rman_scene.depsgraph) + def light_filter_updated(self, obj, input_ob=None): + if input_ob: + ob = input_ob.evaluated_get(self.rman_scene.depsgraph) + else: + ob = obj.id.evaluated_get(self.rman_scene.depsgraph) proto_key = object_utils.prototype_key(ob) - rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) - if not rman_sg_node: + rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) + if not rman_sg_lightfilter: # Light filter needs to be added rman_update = RmanUpdate() rman_update.is_updated_geometry = obj.is_updated_geometry @@ -196,17 +198,20 @@ def light_filter_updated(self, obj): self.rman_updates[ob.original] = rman_update return if obj.is_updated_transform or obj.is_updated_shading: - rfb_log().debug("\tLight Filter: %s Transform Updated" % obj.id.name) - self.light_filter_transform_updated(obj) - if obj.is_updated_geometry: - rfb_log().debug("\tLight Filter: %s Shading Updated" % obj.id.name) + rfb_log().debug("\tLight Filter: %s Transform Updated" % ob.name) + self.light_filter_transform_updated(ob, rman_sg_lightfilter) + if obj.is_updated_geometry or obj.is_updated_shading: + rfb_log().debug("\tLight Filter: %s Shading Updated" % ob.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_node) - for light_ob in rman_sg_node.lights_list: + self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_lightfilter) + for light_ob in rman_sg_lightfilter.lights_list: if isinstance(light_ob, bpy.types.Material): - light_ob.node_tree.update_tag() - else: - light_ob.update_tag() + self.material_updated(light_ob) + elif light_ob.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = True + rman_update.is_updated_transform = True + self.rman_updates[light_ob.original] = rman_update def camera_updated(self, ob_update): ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) @@ -386,7 +391,24 @@ def batch_update_scene(self, context, depsgraph): rman_update.is_updated_geometry = obj.is_updated_geometry rman_update.is_updated_shading = obj.is_updated_shading rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[o.original] = rman_update + self.rman_updates[o.original] = rman_update + + elif isinstance(obj.id, bpy.types.Light): + users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) + for o in users[obj.id.original]: + rman_type = object_utils._detect_primitive_(o) + if rman_type == 'LIGHTFILTER': + self.light_filter_updated(obj, input_ob=o.original) + elif o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = True + rman_update.is_updated_shading = True + self.rman_updates[o.original] = rman_update + + elif isinstance(obj.id, bpy.types.Material): + rfb_log().debug("Material updated: %s" % obj.id.name) + rman_update = RmanUpdate() + self.rman_updates[o.original] = rman_update elif isinstance(obj.id, bpy.types.Object): ob_eval = obj.id.evaluated_get(depsgraph) @@ -400,50 +422,23 @@ def batch_update_scene(self, context, depsgraph): self.update_empty(obj) continue if rman_type == 'LIGHTFILTER': - rfb_log().debug("Light filter: %s updated" % obj.id.name) - ob_eval = obj.id.evaluated_get(self.rman_scene.depsgraph) - proto_key = object_utils.prototype_key(ob_eval) - rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) - if not rman_sg_lightfilter: - continue - if obj.is_updated_geometry: - self.rman_scene.rman_translators['LIGHTFILTER'].update(ob_eval, rman_sg_lightfilter) - for light_ob in rman_sg_lightfilter.lights_list: - if isinstance(light_ob, bpy.types.Material): - ob_key = light_ob.original - else: - ob_key = object_utils.prototype_key(light_ob) - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update - if obj.is_updated_transform: - rman_group_translator = self.rman_scene.rman_translators['GROUP'] - rman_group_translator.update_transform(ob_eval, rman_sg_lightfilter) - + self.light_filter_updated(obj) + continue if rman_type == 'CAMERA': - rfb_log().debug("Camera: %s updated" % obj.id.name) - cam_translator = self.rman_scene.rman_translators['CAMERA'] - rman_sg_camera = self.rman_scene.rman_cameras.get(ob_eval.original) - if obj.is_updated_geometry: - cam_translator.update(ob_eval, rman_sg_camera) - if obj.is_updated_transform: - cam_translator._update_render_cam_transform(ob_eval, rman_sg_camera) + self.camera_updated(obj) continue - rman_update = RmanUpdate() - - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - rfb_log().debug("\tObject: %s Updated" % obj.id.name) rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - self.rman_updates[obj.id.original] = rman_update + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + + if obj.id.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[obj.id.original] = rman_update if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able @@ -504,6 +499,8 @@ def check_instances_batch(self): rman_update = RmanUpdate() rman_update.is_updated_shading = True rman_update.is_updated_transform = True + if rman_sg_node.is_frame_sensitive: + rman_update.is_updated_geometry = True self.rman_updates[ob_key] = rman_update else: if ob_key not in self.rman_updates: @@ -633,6 +630,18 @@ def update_scene(self, context, depsgraph): return ''' + elif isinstance(obj.id, bpy.types.PointLight) or isinstance(obj.id, bpy.types.AreaLight): + users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) + for o in users[obj.id.original]: + rman_type = object_utils._detect_primitive_(o) + if rman_type == 'LIGHTFILTER': + self.light_filter_updated(obj, input_ob=o.original) + elif o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = True + rman_update.is_updated_shading = True + self.rman_updates[o.original] = rman_update + elif isinstance(obj.id, bpy.types.ParticleSettings): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) @@ -694,17 +703,17 @@ def update_scene(self, context, depsgraph): self.camera_updated(obj) continue - rman_update = RmanUpdate() - - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - rfb_log().debug("\tObject: %s Updated" % obj.id.name) rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - self.rman_updates[obj.id.original] = rman_update + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + + if obj.id.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[obj.id.original] = rman_update # Check if this object is the focus object the camera. If it is # we need to update the camera @@ -809,6 +818,8 @@ def check_instances(self): rman_update = RmanUpdate() rman_update.is_updated_shading = True rman_update.is_updated_transform = True + if rman_sg_node.is_frame_sensitive and self.frame_number_changed: + rman_update.is_updated_geometry = True self.rman_updates[ob_key] = rman_update else: if ob_key not in self.rman_updates: From 32f87f27b068c940e38512af5f488140e98d0d51 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 5 May 2022 11:29:26 -0700 Subject: [PATCH 108/278] * consolidate some duplicate code in RmanSceneSync * fix issues with camera not updating when the focus object is translating * remove some old code in ui_object_panels. --- rman_scene.py | 1 + rman_scene_sync.py | 259 ++++++++++----------- rman_sg_nodes/rman_sg_camera.py | 3 + rman_translators/rman_camera_translator.py | 4 + rman_ui/rman_ui_object_panels.py | 2 - 5 files changed, 125 insertions(+), 144 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 55550a22..c33d4b59 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -296,6 +296,7 @@ def export(self): string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) self.bl_frame_current = self.bl_scene.frame_current + string_utils.update_frame_token(self.bl_frame_current) rfb_log().debug("Creating root scene graph node") self.export_root_sg_node() diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 713b19c0..94815053 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -151,20 +151,20 @@ def _mesh_light_update(self, mat): for ob in object_list: ob.update_tag() - def material_updated(self, obj): + def material_updated(self, obj, rman_sg_material=None): if isinstance(obj, bpy.types.DepsgraphUpdate): mat = obj.id else: mat = obj - rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) + if rman_sg_material is None: + rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) translator = self.rman_scene.rman_translators["MATERIAL"] db_name = object_utils.get_db_name(mat) if not rman_sg_material: # Double check if we can't find the material because of an undo rman_sg_material = self.update_materials_dict(mat) - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - mat = obj.id + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): if not rman_sg_material: rfb_log().debug("New material: %s" % mat.name) db_name = object_utils.get_db_name(mat) @@ -182,11 +182,12 @@ def light_filter_transform_updated(self, ob, rman_sg_lightfilter): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_group_translator.update_transform(ob, rman_sg_lightfilter) - def light_filter_updated(self, obj, input_ob=None): - if input_ob: - ob = input_ob.evaluated_get(self.rman_scene.depsgraph) - else: + def light_filter_updated(self, obj, force_update=False): + if isinstance(obj, bpy.types.DepsgraphUpdate): ob = obj.id.evaluated_get(self.rman_scene.depsgraph) + else: + ob = obj.evaluated_get(self.rman_scene.depsgraph) + proto_key = object_utils.prototype_key(ob) rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) if not rman_sg_lightfilter: @@ -197,10 +198,10 @@ def light_filter_updated(self, obj, input_ob=None): rman_update.is_updated_transform = obj.is_updated_transform self.rman_updates[ob.original] = rman_update return - if obj.is_updated_transform or obj.is_updated_shading: + if force_update or obj.is_updated_transform or obj.is_updated_shading: rfb_log().debug("\tLight Filter: %s Transform Updated" % ob.name) self.light_filter_transform_updated(ob, rman_sg_lightfilter) - if obj.is_updated_geometry or obj.is_updated_shading: + if force_update or obj.is_updated_geometry: rfb_log().debug("\tLight Filter: %s Shading Updated" % ob.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_lightfilter) @@ -213,8 +214,11 @@ def light_filter_updated(self, obj, input_ob=None): rman_update.is_updated_transform = True self.rman_updates[light_ob.original] = rman_update - def camera_updated(self, ob_update): - ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) + def camera_updated(self, ob_update, force_update=False): + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) + else: + ob = ob_update with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_sg_camera = self.rman_scene.rman_cameras.get(ob.original) translator = self.rman_scene.rman_translators['CAMERA'] @@ -229,14 +233,14 @@ def camera_updated(self, ob_update): self.rman_scene.sg_scene.Root().AddCoordinateSystem(rman_sg_camera.sg_node) return - if ob_update.is_updated_geometry: + if force_update or ob_update.is_updated_geometry: rfb_log().debug("\tUpdated Camera: %s" % ob.name) if not self.rman_scene.is_viewport_render: translator.update(ob, rman_sg_camera) else: translator.update_viewport_cam(ob, rman_sg_camera, force_update=True) - if ob_update.is_updated_transform: + if force_update or ob_update.is_updated_transform: # we deal with main camera transforms in view_draw if rman_sg_camera == self.rman_scene.main_camera: return @@ -343,6 +347,89 @@ def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): portal.original.update_tag() + def check_light_datablock(self, obj): + if isinstance(obj, bpy.types.DepsgraphUpdate): + ob = obj.id.original + else: + ob = obj.original + users = self.rman_scene.context.blend_data.user_map(subset={ob}, value_types={'OBJECT'}) + for o in users[ob]: + rman_type = object_utils._detect_primitive_(o) + if rman_type == 'LIGHTFILTER': + self.light_filter_updated(o, force_update=True) + elif o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = True + rman_update.is_updated_shading = True + self.rman_updates[o.original] = rman_update + + def check_focus_object(self, ob): + for camera in bpy.data.cameras: + rm = camera.renderman + if rm.rman_focus_object and rm.rman_focus_object.original == ob.original: + users = self.rman_scene.context.blend_data.user_map(subset={camera}) + for o in users[camera]: + self.camera_updated(o.original, force_update=True) + + def check_object_datablock(self, obj): + ob_eval = obj.id.evaluated_get(self.rman_scene.depsgraph) + rman_type = object_utils._detect_primitive_(ob_eval) + + if ob_eval.type in ('ARMATURE'): + return + + # These types need special handling + if rman_type == 'EMPTY': + self.update_empty(obj) + elif rman_type == 'LIGHTFILTER': + self.light_filter_updated(obj) + elif rman_type == 'CAMERA': + self.camera_updated(obj) + else: + rfb_log().debug("\tObject: %s Updated" % obj.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + + if obj.id.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[obj.id.original] = rman_update + + # Check if this object is the focus object the camera. If it is + # we need to update the camera + if obj.is_updated_transform: + self.check_focus_object(ob_eval) + + def check_shader_nodetree(self, obj): + if obj.id.name in bpy.data.node_groups: + if len(obj.id.nodes) < 1: + return + if not obj.id.name.startswith(rman_constants.RMAN_FAKE_NODEGROUP): + return + # this is one of our fake node groups with ramps + # update all of the users of this node tree + rfb_log().debug("ShaderNodeTree updated: %s" % obj.id.name) + users = self.rman_scene.context.blend_data.user_map(subset={obj.id.original}) + for o in users[obj.id.original]: + if self.rman_scene.is_interactive: + if hasattr(o, 'rman_nodetree'): + o.rman_nodetree.update_tag() + elif isinstance(o, bpy.types.Material): + self.update_material(o) + elif isinstance(o, bpy.types.Light): + self.check_light_datablock(o) + elif hasattr(o, 'node_tree'): + o.node_tree.update_tag() + else: + if isinstance(o, bpy.types.Light): + self.check_light_datablock(o) + elif isinstance(o, bpy.types.Material): + rman_update = RmanUpdate() + self.rman_updates[o.original] = rman_update + def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current string_utils.update_frame_token(self.rman_scene.bl_frame_current) @@ -354,6 +441,7 @@ def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_scene = depsgraph.scene_eval self.rman_scene.context = context + # updat the frame number options = self.rman_scene.sg_scene.GetOptions() options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.rman_scene.bl_frame_current) self.rman_scene.sg_scene.SetOptions(options) @@ -394,52 +482,21 @@ def batch_update_scene(self, context, depsgraph): self.rman_updates[o.original] = rman_update elif isinstance(obj.id, bpy.types.Light): - users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) - for o in users[obj.id.original]: - rman_type = object_utils._detect_primitive_(o) - if rman_type == 'LIGHTFILTER': - self.light_filter_updated(obj, input_ob=o.original) - elif o.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = True - rman_update.is_updated_shading = True - self.rman_updates[o.original] = rman_update + self.check_light_datablock(obj) elif isinstance(obj.id, bpy.types.Material): rfb_log().debug("Material updated: %s" % obj.id.name) - rman_update = RmanUpdate() - self.rman_updates[o.original] = rman_update - - elif isinstance(obj.id, bpy.types.Object): - ob_eval = obj.id.evaluated_get(depsgraph) - rman_type = object_utils._detect_primitive_(ob_eval) - - if ob.type in ('ARMATURE'): - continue - - # These types need special handling - if rman_type == 'EMPTY': - self.update_empty(obj) - continue - if rman_type == 'LIGHTFILTER': - self.light_filter_updated(obj) - continue - if rman_type == 'CAMERA': - self.camera_updated(obj) - continue - - rfb_log().debug("\tObject: %s Updated" % obj.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - - if obj.id.original not in self.rman_updates: + ob = obj.id + if ob.original not in self.rman_updates: rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[obj.id.original] = rman_update + self.rman_updates[ob.original] = rman_update + + elif isinstance(obj.id, bpy.types.ShaderNodeTree): + self.check_shader_nodetree(obj) + elif isinstance(obj.id, bpy.types.Object): + self.check_object_datablock(obj) + if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able # to determine what changed. We are forced to check @@ -450,11 +507,10 @@ def batch_update_scene(self, context, depsgraph): self.check_instances_batch() # update any materials - material_translator = self.rman_scene.rman_translators['MATERIAL'] for id, rman_sg_material in self.rman_scene.rman_materials.items(): if rman_sg_material.is_frame_sensitive or id.original in self.rman_updates: mat = id.original - material_translator.update(mat, rman_sg_material) + self.material_updated(mat, rman_sg_material) self.rman_scene.export_integrator() self.rman_scene.export_samplefilters() @@ -551,7 +607,6 @@ def check_instances_batch(self): @time_this def update_scene(self, context, depsgraph): - ## FIXME: this function is waaayyy too big and is doing too much stuff self.rman_updates = dict() self.num_instances_changed = False @@ -607,40 +662,9 @@ def update_scene(self, context, depsgraph): elif isinstance(obj.id, bpy.types.Mesh): rfb_log().debug("Mesh updated: %s" % obj.id.name) - ''' - # Experimental code path. We can use context.blend_data.user_map to ask - # what objects use this mesh. We can then loop thru and call object_update on these - # objects. - # We could also try doing the same thing when we add a new Material. i.e.: - # use user_map to figure out what objects are using this material; however, that would require - # two loops thru user_map - users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) - translator = self.rman_scene.rman_translators['MESH'] - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for o in users[obj.id.original]: - rman_type = object_utils._detect_primitive_(o) - if rman_type != 'MESH': - continue - rman_sg_node = self.rman_scene.rman_objects.get(o.original, None) - translator.update(o, rman_sg_node) - # material slots could have changed, so we need to double - # check that too - for k,v in rman_sg_node.instances.items(): - self.rman_scene.attach_material(o, v) - return - ''' - elif isinstance(obj.id, bpy.types.PointLight) or isinstance(obj.id, bpy.types.AreaLight): - users = context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) - for o in users[obj.id.original]: - rman_type = object_utils._detect_primitive_(o) - if rman_type == 'LIGHTFILTER': - self.light_filter_updated(obj, input_ob=o.original) - elif o.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = True - rman_update.is_updated_shading = True - self.rman_updates[o.original] = rman_update + elif isinstance(obj.id, bpy.types.Light): + self.check_light_datablock(obj) elif isinstance(obj.id, bpy.types.ParticleSettings): rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) @@ -670,60 +694,11 @@ def update_scene(self, context, depsgraph): self.rman_updates[o.original] = rman_update elif isinstance(obj.id, bpy.types.ShaderNodeTree): - if obj.id.name in bpy.data.node_groups: - if len(obj.id.nodes) < 1: - continue - if not obj.id.name.startswith(rman_constants.RMAN_FAKE_NODEGROUP): - continue - # this is one of our fake node groups with ramps - # update all of the users of this node tree - rfb_log().debug("ShaderNodeTree updated: %s" % obj.id.name) - users = context.blend_data.user_map(subset={obj.id.original}) - for o in users[obj.id.original]: - if hasattr(o, 'rman_nodetree'): - o.rman_nodetree.update_tag() - elif hasattr(o, 'node_tree'): - o.node_tree.update_tag() + self.check_shader_nodetree(obj) elif isinstance(obj.id, bpy.types.Object): - ob_eval = obj.id.evaluated_get(depsgraph) - rman_type = object_utils._detect_primitive_(ob_eval) + self.check_object_datablock(obj) - if ob.type in ('ARMATURE'): - continue - - # These types need special handling - if rman_type == 'EMPTY': - self.update_empty(obj) - continue - if rman_type == 'LIGHTFILTER': - self.light_filter_updated(obj) - continue - if ob.type in ['CAMERA']: - self.camera_updated(obj) - continue - - rfb_log().debug("\tObject: %s Updated" % obj.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - - if obj.id.original not in self.rman_updates: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[obj.id.original] = rman_update - - # Check if this object is the focus object the camera. If it is - # we need to update the camera - if obj.is_updated_transform: - for camera in bpy.data.cameras: - rm = camera.renderman - if rm.rman_focus_object and rm.rman_focus_object.original == ob_eval.original: - camera.update_tag() - - elif isinstance(obj.id, bpy.types.Collection): rfb_log().debug("Collection updated: %s" % obj.id.name) #self.update_collection(obj.id) diff --git a/rman_sg_nodes/rman_sg_camera.py b/rman_sg_nodes/rman_sg_camera.py index 766e3fc6..847bbcd3 100644 --- a/rman_sg_nodes/rman_sg_camera.py +++ b/rman_sg_nodes/rman_sg_camera.py @@ -19,6 +19,7 @@ def __init__(self): self.screenwindow = None self.clip_start = -1 self.clip_end = -1 + self.dof_focal_length = -1 def __eq__(self, other): if self.res_width != other.res_width: @@ -55,6 +56,8 @@ def __eq__(self, other): return False if self.rman_fov != other.rman_fov: return False + if self.dof_focal_length != other.dof_focal_length: + return False return True class RmanSgCamera(RmanSgNode): diff --git a/rman_translators/rman_camera_translator.py b/rman_translators/rman_camera_translator.py index 99b3b99c..049d1989 100644 --- a/rman_translators/rman_camera_translator.py +++ b/rman_translators/rman_camera_translator.py @@ -460,18 +460,22 @@ def update_viewport_cam(self, ob, rman_sg_camera, force_update=False): rman_sg_camera.use_focus_object = cam_rm.rman_focus_object if cam_rm.rman_focus_object: dof_focal_distance = (ob.location - cam_rm.rman_focus_object.location).length + rman_sg_camera.bl_cam_props.dof_focal_length = dof_focal_distance rman_sg_node = self.rman_scene.get_rman_prototype(object_utils.prototype_key(cam_rm.rman_focus_object)) if rman_sg_node: rman_sg_camera.rman_focus_object = rman_sg_node else: dof_focal_distance = cam_rm.rman_focus_distance + rman_sg_camera.bl_cam_props.dof_focal_length = dof_focal_distance rman_sg_camera.rman_focus_object = None if dof_focal_distance > 0.0: dof_focal_length = (cam.lens * 0.001) + rman_sg_camera.bl_cam_props.dof_focal_length = dof_focal_distance projparams.SetFloat(self.rman_scene.rman.Tokens.Rix.k_fStop, cam_rm.rman_aperture_fstop) projparams.SetFloat(self.rman_scene.rman.Tokens.Rix.k_focalLength, dof_focal_length) projparams.SetFloat(self.rman_scene.rman.Tokens.Rix.k_focalDistance, dof_focal_distance) else: + rman_sg_camera.bl_cam_props.dof_focal_length = -1 rman_sg_camera.use_focus_object = False rman_sg_camera.rman_focus_object = None diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index 74ffac8c..79ec5687 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -101,7 +101,6 @@ def draw_item(self, layout, context, item): def draw_props(self, layout, context): ob = context.object rm = ob.renderman - anim = rm.archive_anim_settings active = context.active_object rman_type = object_utils._detect_primitive_(active) @@ -698,7 +697,6 @@ def draw(self, context): layout = self.layout ob = context.object rm = ob.renderman - anim = rm.archive_anim_settings active = context.active_object rman_type = object_utils._detect_primitive_(active) From 85aa3e0082f46e64e801cb1c361770f0b97e97f7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 10:21:34 -0700 Subject: [PATCH 109/278] persistent_data should now work with animated preview renders, and blender batch but only if we're using the blender compositor. --- rfb_utils/display_utils.py | 4 +- rman_handlers/__init__.py | 25 +++++++- rman_render.py | 65 ++++++++++++------- rman_scene_sync.py | 128 +++++++++++++++++++------------------ 4 files changed, 133 insertions(+), 89 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 6589cc6c..aebf32ec 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -14,7 +14,7 @@ __BLENDER_TO_RMAN_DSPY__ = { 'TIFF': 'tiff', 'TARGA': 'targa', 'TARGA_RAW': 'targa', 'OPEN_EXR': 'openexr', 'PNG': 'png'} __RMAN_TO_BLENDER__ = { 'tiff': 'TIFF', 'targa': 'TARGA', 'openexr':'OPEN_EXR', 'png':'PNG'} -def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False): +def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False, no_ext=False): view_layer = bpy.context.view_layer rm_rl = None if view_layer.renderman.use_renderman: @@ -27,6 +27,8 @@ def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False): filePath = rm.path_beauty_image_output if use_blender_frame: filePath = re.sub(r'<[f|F]\d*>', '####', filePath) + if no_ext: + filePath = filePath.replace('', '') if rm_rl: aov = rm_rl.custom_aovs[0] display_driver = aov.displaydriver diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index 5f650154..c976dae2 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -1,3 +1,4 @@ +from ..rfb_logger import rfb_log from ..rfb_utils import texture_utils from ..rfb_utils import string_utils from ..rfb_utils import shadergraph_utils @@ -59,7 +60,7 @@ def render_pre(bl_scene): __ORIGINAL_BL_FILE_FORMAT__ = bl_scene.render.image_settings.file_format write_comp = scene_utils.should_use_bl_compositor(bl_scene) if write_comp: - filePath = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True) + filePath = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True, no_ext=True) bl_scene.render.filepath = filePath bl_scene.render.image_settings.file_format = 'OPEN_EXR' else: @@ -88,6 +89,14 @@ def render_post(bl_scene): os.remove(filePath) __BL_TMP_FILE__ = None +@persistent +def render_stop(bl_scene): + from .. import rman_render + rr = rman_render.RmanRender.get_rman_render() + if rr.is_regular_rendering(): + rfb_log().debug("Render stop handler called. Try to stop the renderer.") + rr.stop_render() + def register(): # load_post handler @@ -110,7 +119,13 @@ def register(): bpy.app.handlers.render_pre.append(render_pre) if render_post not in bpy.app.handlers.render_post: - bpy.app.handlers.render_post.append(render_post) + bpy.app.handlers.render_post.append(render_post) + + if render_stop not in bpy.app.handlers.render_complete: + bpy.app.handlers.render_complete.append(render_stop) + + if render_stop not in bpy.app.handlers.render_cancel: + bpy.app.handlers.render_cancel.append(render_stop) def unregister(): @@ -132,5 +147,11 @@ def unregister(): if render_post in bpy.app.handlers.render_post: bpy.app.handlers.render_post.remove(render_post) + if render_stop in bpy.app.handlers.render_complete: + bpy.app.handlers.render_complete.remove(render_stop) + + if render_stop in bpy.app.handlers.render_cancel: + bpy.app.handlers.render_cancel.remove(render_stop) + from . import rman_it_handlers rman_it_handlers.remove_ipr_to_it_handlers() diff --git a/rman_render.py b/rman_render.py index 63d473eb..fa5149a3 100644 --- a/rman_render.py +++ b/rman_render.py @@ -306,7 +306,6 @@ def bl_engine(self, bl_engine): def _start_prman_begin(self): argv = [] argv.append("prman") - #argv.append("-Progress") argv.append("-dspyserver") argv.append("%s" % envconfig().rman_it_path) @@ -464,6 +463,7 @@ def start_render(self, depsgraph, for_background=False): if not self._check_prman_license(): return False + do_persistent_data = rm.do_persistent_data use_compositor = scene_utils.should_use_bl_compositor(self.bl_scene) if for_background: self.rman_render_into = '' @@ -471,6 +471,8 @@ def start_render(self, depsgraph, for_background=False): if use_compositor: self.rman_render_into = 'blender' is_external = False + else: + do_persistent_data = False self.rman_callbacks.clear() ec = rman.EventCallbacks.Get() ec.RegisterCallback("Render", render_cb, self) @@ -478,7 +480,8 @@ def start_render(self, depsgraph, for_background=False): if envconfig().getenv('RFB_BATCH_NO_PROGRESS') is None: ec.RegisterCallback("Progress", batch_progress_cb, self) self.rman_callbacks["Progress"] = batch_progress_cb - rman.Dspy.DisableDspyServer() + rman.Dspy.DisableDspyServer() + rman.Dspy.EnableDspyServer() else: self.rman_render_into = rm.render_into @@ -504,27 +507,36 @@ def start_render(self, depsgraph, for_background=False): scene_utils.set_render_variant_config(self.bl_scene, config, render_config) self.rman_is_xpu = (rendervariant == 'xpu') - self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) + boot_strapping = False + if self.sg_scene is None: + boot_strapping = True + self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) was_connected = self.stats_mgr.is_connected() - if self.rman_is_xpu: - if not was_connected: - # force the stats to start in the case of XPU - # this is so that we can get a progress percentage - # if we can't get it to start, abort - self.stats_mgr.attach(force=True) - time.sleep(0.5) # give it a second to attach - if not self.stats_mgr.is_connected(): - self.bl_engine.report({'ERROR'}, 'Cannot start live stats. Aborting XPU render') - self.stop_render(stop_draw_thread=False) - self.del_bl_engine() - return False + if self.rman_is_xpu and not was_connected: + # force the stats to start in the case of XPU + # this is so that we can get a progress percentage + # if we can't get it to start, abort + self.stats_mgr.attach(force=True) + time.sleep(0.5) # give it a second to attach + if not self.stats_mgr.is_connected(): + self.bl_engine.report({'ERROR'}, 'Cannot start live stats. Aborting XPU render') + self.stop_render(stop_draw_thread=False) + self.del_bl_engine() + return False + # Export the scene try: bl_layer = depsgraph.view_layer self.rman_is_exporting = True self.rman_running = True self.start_export_stats_thread() - self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_layer, is_external=is_external) + if boot_strapping: + # This is our first time exporting + self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_layer, is_external=is_external) + else: + # Scene still exists, which means we're in persistent data mode + # Try to get the scene diffs. + self.rman_scene_sync.batch_update_scene(bpy.context, depsgraph) self.rman_is_exporting = False self.stats_mgr.reset_progress() @@ -538,11 +550,13 @@ def start_render(self, depsgraph, for_background=False): self.del_bl_engine() return False + # Start the render render_cmd = "prman" if self.rman_render_into == 'blender': render_cmd = "prman -live" render_cmd = self._append_render_cmd(render_cmd) - self.sg_scene.Render(render_cmd) + if boot_strapping: + self.sg_scene.Render(render_cmd) if self.rman_render_into == 'blender': dspy_dict = display_utils.get_dspy_dict(self.rman_scene, include_holdouts=False) @@ -645,17 +659,22 @@ def start_render(self, depsgraph, for_background=False): pass finally: bpy.data.images.remove(bl_image) - - if not was_connected and self.stats_mgr.is_connected(): - # if stats were not started before rendering, disconnect - self.stats_mgr.disconnect() + else: self.start_stats_thread() while self.bl_engine and not self.bl_engine.test_break() and self.rman_is_live_rendering: - time.sleep(0.01) + time.sleep(0.01) + + if not was_connected and self.stats_mgr.is_connected(): + # if stats was not started before rendering, disconnect + self.stats_mgr.disconnect() self.del_bl_engine() - self.stop_render() + if not do_persistent_data: + # If we're not in persistent data mode, stop the renderer immediately + # If we are in persistent mode, we rely on the render_complete and + # render_cancel handlers to stop the renderer (see rman_handlers/__init__.py) + self.stop_render() return True diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 94815053..ecabf128 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -430,6 +430,7 @@ def check_shader_nodetree(self, obj): rman_update = RmanUpdate() self.rman_updates[o.original] = rman_update + @time_this def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current string_utils.update_frame_token(self.rman_scene.bl_frame_current) @@ -453,72 +454,73 @@ def batch_update_scene(self, context, depsgraph): self.num_instances_changed = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) - for obj in reversed(depsgraph.updates): - ob = obj.id - - if isinstance(obj.id, bpy.types.ParticleSettings): - rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) - for o in self.rman_scene.bl_scene.objects: - psys = None - ob = o.evaluated_get(depsgraph) - for ps in ob.particle_systems: - if ps.settings.original == obj.id.original: - psys = ps - break - if not psys: - continue - if object_utils.is_particle_instancer(psys): - #self.check_particle_instancer(obj, psys) - pass - else: - self.update_particle_emitter(ob, psys) - - if o.original not in self.rman_updates: - rman_update = RmanUpdate() + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + for obj in reversed(depsgraph.updates): + ob = obj.id - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[o.original] = rman_update + if isinstance(obj.id, bpy.types.ParticleSettings): + rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) + for o in self.rman_scene.bl_scene.objects: + psys = None + ob = o.evaluated_get(depsgraph) + for ps in ob.particle_systems: + if ps.settings.original == obj.id.original: + psys = ps + break + if not psys: + continue + if object_utils.is_particle_instancer(psys): + #self.check_particle_instancer(obj, psys) + pass + else: + self.update_particle_emitter(ob, psys) - elif isinstance(obj.id, bpy.types.Light): - self.check_light_datablock(obj) + if o.original not in self.rman_updates: + rman_update = RmanUpdate() - elif isinstance(obj.id, bpy.types.Material): - rfb_log().debug("Material updated: %s" % obj.id.name) - ob = obj.id - if ob.original not in self.rman_updates: - rman_update = RmanUpdate() - self.rman_updates[ob.original] = rman_update + rman_update.is_updated_geometry = obj.is_updated_geometry + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[o.original] = rman_update - elif isinstance(obj.id, bpy.types.ShaderNodeTree): - self.check_shader_nodetree(obj) + elif isinstance(obj.id, bpy.types.Light): + self.check_light_datablock(obj) - elif isinstance(obj.id, bpy.types.Object): - self.check_object_datablock(obj) - - if not self.rman_updates and self.num_instances_changed: - # The number of instances changed, but we are not able - # to determine what changed. We are forced to check - # all instances. - rfb_log().debug("Set check_all_instances to True") - self.check_all_instances = True - - self.check_instances_batch() - - # update any materials - for id, rman_sg_material in self.rman_scene.rman_materials.items(): - if rman_sg_material.is_frame_sensitive or id.original in self.rman_updates: - mat = id.original - self.material_updated(mat, rman_sg_material) - - self.rman_scene.export_integrator() - self.rman_scene.export_samplefilters() - self.rman_scene.export_displayfilters() - self.rman_scene.export_displays() - - if self.rman_scene.do_motion_blur and self.rman_scene.moving_objects: - self.rman_scene.export_instances_motion() + elif isinstance(obj.id, bpy.types.Material): + rfb_log().debug("Material updated: %s" % obj.id.name) + ob = obj.id + if ob.original not in self.rman_updates: + rman_update = RmanUpdate() + self.rman_updates[ob.original] = rman_update + + elif isinstance(obj.id, bpy.types.ShaderNodeTree): + self.check_shader_nodetree(obj) + + elif isinstance(obj.id, bpy.types.Object): + self.check_object_datablock(obj) + + if not self.rman_updates and self.num_instances_changed: + # The number of instances changed, but we are not able + # to determine what changed. We are forced to check + # all instances. + rfb_log().debug("Set check_all_instances to True") + self.check_all_instances = True + + self.check_instances_batch() + + # update any materials + for id, rman_sg_material in self.rman_scene.rman_materials.items(): + if rman_sg_material.is_frame_sensitive or id.original in self.rman_updates: + mat = id.original + self.material_updated(mat, rman_sg_material) + + self.rman_scene.export_integrator() + self.rman_scene.export_samplefilters() + self.rman_scene.export_displayfilters() + self.rman_scene.export_displays() + + if self.rman_scene.do_motion_blur and self.rman_scene.moving_objects: + self.rman_scene.export_instances_motion() @time_this def check_instances_batch(self): @@ -588,7 +590,7 @@ def check_instances_batch(self): if rman_type in object_utils._RMAN_NO_INSTANCES_: continue - + # clear all instances for this prototype, if # we have not already done so if instance_parent: @@ -604,7 +606,7 @@ def check_instances_batch(self): clear_instances.append(rman_sg_node) self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) - + @time_this def update_scene(self, context, depsgraph): From e858ef77f6b801462a63ee45a601f51fd36d5607 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 11:49:23 -0700 Subject: [PATCH 110/278] We should now be able to use persistent_data mode with blender batch. * if we are persistent_data mode, we need to be in "-live" render mode * only use the render_complete and render_cancel handlers for non-background mode. for some reason render_complete is getting call after each rendered frame in background mode. --- rman_handlers/__init__.py | 9 +++++---- rman_render.py | 7 ++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index c976dae2..8d7c496f 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -121,11 +121,12 @@ def register(): if render_post not in bpy.app.handlers.render_post: bpy.app.handlers.render_post.append(render_post) - if render_stop not in bpy.app.handlers.render_complete: - bpy.app.handlers.render_complete.append(render_stop) + if not bpy.app.background: + if render_stop not in bpy.app.handlers.render_complete: + bpy.app.handlers.render_complete.append(render_stop) - if render_stop not in bpy.app.handlers.render_cancel: - bpy.app.handlers.render_cancel.append(render_stop) + if render_stop not in bpy.app.handlers.render_cancel: + bpy.app.handlers.render_cancel.append(render_stop) def unregister(): diff --git a/rman_render.py b/rman_render.py index fa5149a3..a64b3fd0 100644 --- a/rman_render.py +++ b/rman_render.py @@ -471,8 +471,6 @@ def start_render(self, depsgraph, for_background=False): if use_compositor: self.rman_render_into = 'blender' is_external = False - else: - do_persistent_data = False self.rman_callbacks.clear() ec = rman.EventCallbacks.Get() ec.RegisterCallback("Render", render_cb, self) @@ -480,8 +478,7 @@ def start_render(self, depsgraph, for_background=False): if envconfig().getenv('RFB_BATCH_NO_PROGRESS') is None: ec.RegisterCallback("Progress", batch_progress_cb, self) self.rman_callbacks["Progress"] = batch_progress_cb - rman.Dspy.DisableDspyServer() - rman.Dspy.EnableDspyServer() + rman.Dspy.DisableDspyServer() else: self.rman_render_into = rm.render_into @@ -552,7 +549,7 @@ def start_render(self, depsgraph, for_background=False): # Start the render render_cmd = "prman" - if self.rman_render_into == 'blender': + if self.rman_render_into == 'blender' or do_persistent_data: render_cmd = "prman -live" render_cmd = self._append_render_cmd(render_cmd) if boot_strapping: From 45c0af9121b65f646911c4236d6e7559301453b1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 12:02:23 -0700 Subject: [PATCH 111/278] Make sure the channel selector doesn't show the holdout channels, unless the holdout is in a separate AOV. --- rfb_utils/display_utils.py | 46 ++++++++++++++++++++----------------- rman_ui/rman_ui_viewport.py | 2 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index aebf32ec..158c9603 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -501,7 +501,7 @@ def _set_rman_dspy_dict(rm_rl, dspys_dict, dspy_drv, rman_scene, expandTokens, d 'params': dspy_params, 'dspyDriverParams': None} -def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens): +def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens, include_holdouts=True): rm = rman_scene.bl_scene.renderman display_driver = dspy_drv @@ -512,24 +512,28 @@ def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens) if display_driver == 'openexr': param_list.SetInteger('asrgba', 1) - dspy_params = {} - dspy_params['displayChannels'] = [] - d = _default_dspy_params() - occluded_src = "color lpe:holdouts;C[DS]+" - d[u'channelSource'] = {'type': u'string', 'value': occluded_src} - d[u'channelType'] = { 'type': u'string', 'value': 'color'} - dspys_dict['channels']['occluded'] = d - dspy_params['displayChannels'].append('occluded') - - dspys_dict['displays']['occluded'] = { - 'driverNode': 'null', - 'filePath': 'occluded', - 'denoise': False, - 'denoise_mode': 'singleframe', - 'camera': None, - 'bake_mode': None, - 'params': dspy_params, - 'dspyDriverParams': None} + if include_holdouts: + dspy_params = {} + dspy_params['displayChannels'] = [] + d = _default_dspy_params() + occluded_src = "color lpe:holdouts;C[DS]+" + d[u'channelSource'] = {'type': u'string', 'value': occluded_src} + d[u'channelType'] = { 'type': u'string', 'value': 'color'} + dspys_dict['channels']['occluded'] = d + dspy_params['displayChannels'].append('occluded') + + dspys_dict['displays']['occluded'] = { + 'driverNode': 'null', + 'filePath': 'occluded', + 'denoise': False, + 'denoise_mode': 'singleframe', + 'camera': None, + 'bake_mode': None, + 'params': dspy_params, + 'dspyDriverParams': None} + + if rm.do_holdout_matte != "AOV" and not include_holdouts: + return dspy_params = {} dspy_params['displayChannels'] = [] @@ -664,8 +668,8 @@ def get_dspy_dict(rman_scene, expandTokens=True, include_holdouts=True): # We're using blender's layering system _set_blender_dspy_dict(layer, dspys_dict, display_driver, rman_scene, expandTokens, do_optix_denoise=do_optix_denoise) - if rm.do_holdout_matte != "OFF" and include_holdouts: - _set_rman_holdouts_dspy_dict(dspys_dict, display_driver, rman_scene, expandTokens) + if rm.do_holdout_matte != "OFF": + _set_rman_holdouts_dspy_dict(dspys_dict, display_driver, rman_scene, expandTokens, include_holdouts=include_holdouts) return dspys_dict diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 8ef830c9..b988f0bf 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -95,7 +95,7 @@ def draw(self, context): layout = self.layout rman_render = RmanRender.get_rman_render() rman_render.rman_scene._find_renderman_layer() - dspys_dict = display_utils.get_dspy_dict(rman_render.rman_scene) + dspys_dict = display_utils.get_dspy_dict(rman_render.rman_scene, include_holdouts=False) for chan_name, chan_params in dspys_dict['channels'].items(): layout.operator_context = 'EXEC_DEFAULT' op = layout.operator('renderman_viewport.channel_selector', text=chan_name) From e4fe5cf8f1998712f5bff23222eddcbcf1c80ba0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 13:18:11 -0700 Subject: [PATCH 112/278] Move the code that sets up/updates the Blender render result into a helper class. --- rman_render.py | 228 +++++++++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 104 deletions(-) diff --git a/rman_render.py b/rman_render.py index a64b3fd0..9304f57f 100644 --- a/rman_render.py +++ b/rman_render.py @@ -242,6 +242,119 @@ def preload_xpu(): rfb_log().debug('Failed to preload xpu: {0}'.format(error)) return None +class BlRenderResultHelper: + def __init__(self, rman_render, dspy_dict): + self.rman_render = rman_render + self.dspy_dict = dspy_dict + self.width = -1 + self.height = -1 + self.size_x = -1 + self.size_y = -1 + self.bl_result = None + self.bl_image_rps = dict() + self.render = None + self.render_view = None + self.image_scale = -1 + self.write_aovs = False + + def register_passes(self): + self.render = self.rman_render.rman_scene.bl_scene.render + self.render_view = self.rman_render.bl_engine.active_view_get() + self.image_scale = self.render.resolution_percentage / 100.0 + self.width = int(self.render.resolution_x * self.image_scale) + self.height = int(self.render.resolution_y * self.image_scale) + + # register any AOV's as passes + for i, dspy_nm in enumerate(self.dspy_dict['displays'].keys()): + if i == 0: + continue + + num_channels = -1 + while num_channels == -1: + num_channels = self.rman_render.get_numchannels(i) + + dspy = self.dspy_dict['displays'][dspy_nm] + dspy_chan = dspy['params']['displayChannels'][0] + chan_info = self.dspy_dict['channels'][dspy_chan] + chan_type = chan_info['channelType']['value'] + + if num_channels == 4: + self.rman_render.bl_engine.add_pass(dspy_nm, 4, 'RGBA') + elif num_channels == 3: + if chan_type == 'color': + self.rman_render.bl_engine.add_pass(dspy_nm, 3, 'RGB') + else: + self.rman_render.bl_engine.add_pass(dspy_nm, 3, 'XYZ') + elif num_channels == 2: + self.rman_render.bl_engine.add_pass(dspy_nm, 2, 'XY') + else: + self.rman_render.bl_engine.add_pass(dspy_nm, 1, 'X') + + self.size_x = self.width + self.size_y = self.height + if self.render.use_border: + self.size_x = int(self.width * (self.render.border_max_x - self.render.border_min_x)) + self.size_y = int(self.height * (self.render.border_max_y - self.render.border_min_y)) + + self.bl_result = self.rman_render.bl_engine.begin_result(0, 0, + self.size_x, + self.size_y, + view=self.render_view) + + for i, dspy_nm in enumerate(self.dspy_dict['displays'].keys()): + if i == 0: + render_pass = self.bl_result.layers[0].passes.find_by_name("Combined", self.render_view) + else: + render_pass = self.bl_result.layers[0].passes.find_by_name(dspy_nm, self.render_view) + self.bl_image_rps[i] = render_pass + + def update_passes(self): + for i, rp in self.bl_image_rps.items(): + buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, + num_channels=rp.channels, + as_flat=False, + back_fill=False, + render=self.render) + if buffer is None: + continue + rp.rect = buffer + + if self.rman_render.bl_engine: + self.rman_render.bl_engine.update_result(self.bl_result) + + def finish_passes(self): + if self.bl_result: + if self.rman_render.bl_engine: + self.rman_render.bl_engine.end_result(self.bl_result) + + # check if we should write out the AOVs + if self.write_aovs: + for i, dspy_nm in enumerate(self.dspy_dict['displays'].keys()): + filepath = self.dspy_dict['displays'][dspy_nm]['filePath'] + buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, as_flat=True) + if buffer is None: + continue + + if i == 0: + # write out the beauty with a 'raw' substring + toks = os.path.splitext(filepath) + filepath = '%s_beauty_raw.exr' % (toks[0]) + + bl_image = bpy.data.images.new(dspy_nm, self.width, self.height) + try: + if isinstance(buffer, numpy.ndarray): + buffer = buffer.tolist() + bl_image.use_generated_float = True + bl_image.filepath_raw = filepath + bl_image.pixels.foreach_set(buffer) + bl_image.file_format = 'OPEN_EXR' + bl_image.update() + bl_image.save() + except: + pass + finally: + bpy.data.images.remove(bl_image) + class RmanRender(object): ''' RmanRender class. This class is responsible for starting and stopping @@ -505,6 +618,7 @@ def start_render(self, depsgraph, for_background=False): self.rman_is_xpu = (rendervariant == 'xpu') boot_strapping = False + bl_rr_helper = None if self.sg_scene is None: boot_strapping = True self.sg_scene = self.sgmngr.CreateScene(config, render_config, self.stats_mgr.rman_stats_session) @@ -556,111 +670,17 @@ def start_render(self, depsgraph, for_background=False): self.sg_scene.Render(render_cmd) if self.rman_render_into == 'blender': dspy_dict = display_utils.get_dspy_dict(self.rman_scene, include_holdouts=False) - - render = self.rman_scene.bl_scene.render - render_view = self.bl_engine.active_view_get() - image_scale = render.resolution_percentage / 100.0 - width = int(render.resolution_x * image_scale) - height = int(render.resolution_y * image_scale) - - bl_image_rps= dict() - - # register any AOV's as passes - for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): - if i == 0: - continue - - num_channels = -1 - while num_channels == -1: - num_channels = self.get_numchannels(i) - - dspy = dspy_dict['displays'][dspy_nm] - dspy_chan = dspy['params']['displayChannels'][0] - chan_info = dspy_dict['channels'][dspy_chan] - chan_type = chan_info['channelType']['value'] - - if num_channels == 4: - self.bl_engine.add_pass(dspy_nm, 4, 'RGBA') - elif num_channels == 3: - if chan_type == 'color': - self.bl_engine.add_pass(dspy_nm, 3, 'RGB') - else: - self.bl_engine.add_pass(dspy_nm, 3, 'XYZ') - elif num_channels == 2: - self.bl_engine.add_pass(dspy_nm, 2, 'XY') - else: - self.bl_engine.add_pass(dspy_nm, 1, 'X') - - size_x = width - size_y = height - if render.use_border: - size_x = int(width * (render.border_max_x - render.border_min_x)) - size_y = int(height * (render.border_max_y - render.border_min_y)) - - result = self.bl_engine.begin_result(0, 0, - size_x, - size_y, - view=render_view) - - for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): - if i == 0: - render_pass = result.layers[0].passes.find_by_name("Combined", render_view) - else: - render_pass = result.layers[0].passes.find_by_name(dspy_nm, render_view) - bl_image_rps[i] = render_pass - - self.start_stats_thread() - while self.bl_engine and not self.bl_engine.test_break() and self.rman_is_live_rendering: - time.sleep(0.01) - for i, rp in bl_image_rps.items(): - buffer = self._get_buffer(width, height, image_num=i, - num_channels=rp.channels, - as_flat=False, - back_fill=False, - render=render) - if buffer is None: - continue - rp.rect = buffer - - if self.bl_engine: - self.bl_engine.update_result(result) - - if result: - if self.bl_engine: - self.bl_engine.end_result(result) - - # check if we should write out the AOVs - if use_compositor and rm.use_bl_compositor_write_aovs: - for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): - filepath = dspy_dict['displays'][dspy_nm]['filePath'] - buffer = self._get_buffer(width, height, image_num=i, as_flat=True) - if buffer is None: - continue - - if i == 0: - # write out the beauty with a 'raw' substring - toks = os.path.splitext(filepath) - filepath = '%s_beauty_raw.exr' % (toks[0]) - - bl_image = bpy.data.images.new(dspy_nm, width, height) - try: - if isinstance(buffer, numpy.ndarray): - buffer = buffer.tolist() - bl_image.use_generated_float = True - bl_image.filepath_raw = filepath - bl_image.pixels.foreach_set(buffer) - bl_image.file_format = 'OPEN_EXR' - bl_image.update() - bl_image.save() - except: - pass - finally: - bpy.data.images.remove(bl_image) + bl_rr_helper = BlRenderResultHelper(self, dspy_dict) + bl_rr_helper.write_aovs = (use_compositor and rm.use_bl_compositor_write_aovs) + bl_rr_helper.register_passes() - else: - self.start_stats_thread() - while self.bl_engine and not self.bl_engine.test_break() and self.rman_is_live_rendering: - time.sleep(0.01) + self.start_stats_thread() + while self.bl_engine and not self.bl_engine.test_break() and self.rman_is_live_rendering: + time.sleep(0.01) + if bl_rr_helper: + bl_rr_helper.update_passes() + if bl_rr_helper: + bl_rr_helper.finish_passes() if not was_connected and self.stats_mgr.is_connected(): # if stats was not started before rendering, disconnect From 2d84916850768db7af9ee637d7755c886f5319ac Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 15:10:54 -0700 Subject: [PATCH 113/278] * modify should_use_bl_compositor to return false if we're rendering to it * camera wasn't getting updated when persistent_data was turned on. --- rfb_utils/scene_utils.py | 4 ++-- rman_handlers/__init__.py | 1 - rman_scene_sync.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index f843591e..7b8f83ed 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -55,10 +55,10 @@ def should_use_bl_compositor(bl_scene): Returns: (bool) - true if we should use the compositor; false if not ''' + rm = bl_scene.renderman if not bpy.app.background: - return True + return (rm.render_into == 'blender') - rm = bl_scene.renderman if not rm.use_bl_compositor: # explicitiy turned off return False diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index 8d7c496f..b1cf5961 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -79,7 +79,6 @@ def render_post(bl_scene): global __ORIGINAL_BL_FILEPATH__ global __ORIGINAL_BL_FILE_FORMAT__ global __BL_TMP_FILE__ - from ..rfb_utils import display_utils bl_scene.render.filepath = __ORIGINAL_BL_FILEPATH__ bl_scene.render.image_settings.file_format = __ORIGINAL_BL_FILE_FORMAT__ diff --git a/rman_scene_sync.py b/rman_scene_sync.py index ecabf128..a770c1db 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -242,7 +242,7 @@ def camera_updated(self, ob_update, force_update=False): if force_update or ob_update.is_updated_transform: # we deal with main camera transforms in view_draw - if rman_sg_camera == self.rman_scene.main_camera: + if self.rman_scene.is_viewport_render: return if translator._update_render_cam_transform(ob, rman_sg_camera): rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) From b4986f272824cc3b0f03bc81ecb07d4af7b8851b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 6 May 2022 15:40:34 -0700 Subject: [PATCH 114/278] Remove the max limit on the frame chunking and have rman_spool use the minimum of chunk value and the last frame. --- rman_config/config/rman_properties_scene.json | 1 - rman_spool.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index f6b58df9..e1dddb3f 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1314,7 +1314,6 @@ "type": "int", "default": 1, "min": 1, - "max": 10, "help": "Frame chunking. Determines how many frames each Blender batch task will render.", "conditionalVisOps": { "conditionalVisOp": "equalTo", diff --git a/rman_spool.py b/rman_spool.py index 5634cb57..71fa5cda 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -140,7 +140,7 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, (str(self.depsgraph.view_layer.name))) renderframestask.title = renderframestasktitle - if chunk > 1 and by < 2 and (start+chunk < last+1): + if (chunk+1) > 1 and by < 2 and (start+chunk < last+1): iframe = start while (iframe < last+1): diff = chunk @@ -341,7 +341,7 @@ def blender_batch_render(self, bl_filename): frame_begin = self.bl_scene.frame_start frame_end = self.bl_scene.frame_end by = self.bl_scene.frame_step - chunk = rm.bl_batch_frame_chunk-1 + chunk = min(rm.bl_batch_frame_chunk, frame_end)-1 # update variables string_utils.set_var('scene', self.bl_scene.name) From 847891b59e735077abee33b11785412327996e6e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 12 May 2022 11:25:32 -0700 Subject: [PATCH 115/278] For RMAN-17801. Rename our sockets to match the names in RfH and RfK (ex: Bxdf -> bxdf_out/bxdf_in). This is to be consistent with the names used in USD. --- rfb_utils/draw_utils.py | 6 +- rfb_utils/upgrade_utils.py | 67 ++++++++++++++++++++ rman_bl_nodes/__init__.py | 16 ++--- rman_bl_nodes/rman_bl_nodes_shaders.py | 24 +++---- rman_cycles_convert/__init__.py | 40 ++++++------ rman_cycles_convert/cycles_convert.py | 34 +++++----- rman_operators/rman_operators_view3d.py | 2 +- rman_presets/core.py | 26 ++++---- rman_translators/rman_material_translator.py | 6 +- rman_ui/rman_ui_camera_panels.py | 2 +- rman_ui/rman_ui_material_panels.py | 41 ++++++------ rman_ui/rman_ui_object_panels.py | 20 +++--- rman_ui/rman_ui_render_panels.py | 6 +- rman_ui/rman_ui_world_panels.py | 2 +- 14 files changed, 183 insertions(+), 109 deletions(-) diff --git a/rfb_utils/draw_utils.py b/rfb_utils/draw_utils.py index 79bd095d..25c2c3e5 100644 --- a/rfb_utils/draw_utils.py +++ b/rfb_utils/draw_utils.py @@ -475,7 +475,7 @@ def panel_node_draw(layout, context, id_data, output_type, input_name): return True -def draw_nodes_properties_ui(layout, context, nt, input_name='Bxdf', +def draw_nodes_properties_ui(layout, context, nt, input_name='bxdf_in', output_node_type="output"): output_node = next((n for n in nt.nodes if hasattr(n, 'renderman_node_type') and n.renderman_node_type == output_node_type), None) @@ -489,9 +489,9 @@ def draw_nodes_properties_ui(layout, context, nt, input_name='Bxdf', layout.context_pointer_set("node", output_node) layout.context_pointer_set("socket", socket) - if input_name not in ['Light', 'LightFilter']: + if input_name not in ['light_in', 'lightfilter_in']: split = layout.split(factor=0.35) - split.label(text=socket.name + ':') + split.label(text=socket.identifier + ':') split.context_pointer_set("socket", socket) split.context_pointer_set("node", output_node) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index 005b3fef..d2f8b91e 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -22,10 +22,77 @@ def upgrade_243(scene): elem.name = '%s[%d]' % (prop_name, len(collection)-1) elem.type = param_array_type +def upgrade_250(scene): + ''' + Rename input/output sockets: + Bxdf -> bxdf_in/bxdf_out + Light -> light_in/light_out + Displacement -> displace_in/displace_out + LightFilter -> lightfilter_in/lightfilter_out + Integrator -> integrator_in/integrator_out + Projection -> projection_in/projection_out + DisplayFilter -> displayfilter_in/displayfilter_out + SampleFilter -> samplefilter_in/samplefilter_out + ''' + for node in shadergraph_utils.get_all_shading_nodes(scene=scene): + renderman_node_type = getattr(node, 'renderman_node_type', '') + if renderman_node_type in ['bxdf', 'projection', 'light', 'integrator']: + old_name = renderman_node_type.capitalize() + if old_name in node.outputs: + node.outputs[old_name].name = '%s_out' % renderman_node_type + elif renderman_node_type in ['displace', 'displacement']: + node.outputs['Displacement'].name = 'displace_out' + elif renderman_node_type == 'lightfilter': + if 'LightFilter' in node.outputs: + node.outputs['LightFilter'].name = '%s_out' % renderman_node_type + elif renderman_node_type == 'samplefilter': + if 'SampleFilter' in node.outputs: + node.outputs['SampleFilter'].name = '%s_out' % renderman_node_type + elif renderman_node_type == 'displayfilter': + if 'DisplayFilter' in node.outputs: + node.outputs['DisplayFilter'].name = '%s_out' % renderman_node_type + + for mat in bpy.data.materials: + output = shadergraph_utils.find_node(mat, 'RendermanOutputNode') + if output: + output.inputs['Bxdf'].name = 'bxdf_in' + output.inputs['Light'].name = 'light_in' + output.inputs['Displacement'].name = 'displace_in' + output.inputs['LightFilter'].name = 'lightfilter_in' + + for light in bpy.data.lights: + output = shadergraph_utils.is_renderman_nodetree(light) + if output: + output.inputs['Light'].name = 'light_in' + output.inputs['LightFilter'].name = 'lightfilter_in' + + for world in bpy.data.worlds: + output = shadergraph_utils.find_node(world, 'RendermanIntegratorsOutputNode') + if output: + output.inputs['Integrator'].name = 'integrator_in' + + output = shadergraph_utils.find_node(world, 'RendermanSamplefiltersOutputNode') + if output: + for i, o in enumerate(output.inputs): + o.name = 'samplefilter_in[%d]' % i + + output = shadergraph_utils.find_node(world, 'RendermanDisplayfiltersOutputNode') + if output: + for i, o in enumerate(output.inputs): + o.name = 'displayfilter_in[%d]' % i + + for camera in bpy.data.cameras: + nt = camera.renderman.rman_nodetree + if nt: + output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanProjectionsOutputNode') + output.inputs['Projection'].name = 'projection_in' + + __RMAN_SCENE_UPGRADE_FUNCTIONS__ = OrderedDict() __RMAN_SCENE_UPGRADE_FUNCTIONS__['24.2'] = upgrade_242 __RMAN_SCENE_UPGRADE_FUNCTIONS__['24.3'] = upgrade_243 +__RMAN_SCENE_UPGRADE_FUNCTIONS__['25.0'] = upgrade_250 def upgrade_scene(bl_scene): global __RMAN_SCENE_UPGRADE_FUNCTIONS__ diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index 09bd8ccc..ef0c9a5e 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -326,29 +326,29 @@ def generate_node_type(node_desc, is_oso=False): def init(self, context): # add input/output sockets to nodes, based on type if self.renderman_node_type == 'bxdf': - self.outputs.new('RendermanNodeSocketBxdf', "Bxdf") + self.outputs.new('RendermanNodeSocketBxdf', "bxdf_out", identifier="Bxdf") node_add_inputs(self, name, self.prop_names) node_add_outputs(self) elif self.renderman_node_type == 'light': node_add_inputs(self, name, self.prop_names) - self.outputs.new('RendermanNodeSocketLight', "Light") + self.outputs.new('RendermanNodeSocketLight', "light_out", identifier="Light") elif self.renderman_node_type == 'lightfilter': node_add_inputs(self, name, self.prop_names) - self.outputs.new('RendermanNodeSocketLightFilter', "LightFilter") + self.outputs.new('RendermanNodeSocketLightFilter', "lightfilter_out", idenitifer="LightFilter") elif self.renderman_node_type == 'displace': - self.outputs.new('RendermanNodeSocketDisplacement', "Displacement") + self.outputs.new('RendermanNodeSocketDisplacement', "displace_out", identifier="Displacement") node_add_inputs(self, name, self.prop_names) elif self.renderman_node_type == 'displayfilter': - self.outputs.new('RendermanNodeSocketDisplayFilter', "DisplayFilter") + self.outputs.new('RendermanNodeSocketDisplayFilter', "displayfilter_out", identifier="DisplayFilter") node_add_inputs(self, name, self.prop_names) elif self.renderman_node_type == 'samplefilter': - self.outputs.new('RendermanNodeSocketSampleFilter', "SampleFilter") + self.outputs.new('RendermanNodeSocketSampleFilter', "samplefilter_out", identifier="SampleFilter") node_add_inputs(self, name, self.prop_names) elif self.renderman_node_type == 'integrator': - self.outputs.new('RendermanNodeSocketIntegrator', "Integrator") + self.outputs.new('RendermanNodeSocketIntegrator', "integrator_out", identifier="Integrator") node_add_inputs(self, name, self.prop_names) elif self.renderman_node_type == 'projection': - self.outputs.new('RendermanNodeSocketProjection', "Projection") + self.outputs.new('RendermanNodeSocketProjection', "projection_out", identifier="Projection") node_add_inputs(self, name, self.prop_names) elif name == "PxrOSL": self.outputs.clear() diff --git a/rman_bl_nodes/rman_bl_nodes_shaders.py b/rman_bl_nodes/rman_bl_nodes_shaders.py index 355b189d..592f6554 100644 --- a/rman_bl_nodes/rman_bl_nodes_shaders.py +++ b/rman_bl_nodes/rman_bl_nodes_shaders.py @@ -28,7 +28,7 @@ class RendermanShadingNode(bpy.types.ShaderNode): prev_hidden: BoolProperty(default=False, description="Whether or not this node was previously hidden.") def update_mat(self, mat): - if self.renderman_node_type == 'bxdf' and self.outputs['Bxdf'].is_linked: + if self.renderman_node_type == 'bxdf' and self.outputs['bxdf_out'].is_linked: mat.specular_color = [1, 1, 1] mat.diffuse_color = [1, 1, 1, 1] mat.specular_intensity = 0 @@ -685,13 +685,13 @@ def init(self, context): self._init_inputs() def _init_inputs(self): - input = self.inputs.new('RendermanNodeSocketBxdf', 'Bxdf') + input = self.inputs.new('RendermanNodeSocketBxdf', 'bxdf_in', identifier='Bxdf') input.hide_value = True - input = self.inputs.new('RendermanNodeSocketLight', 'Light') + input = self.inputs.new('RendermanNodeSocketLight', 'light_in', identifier='Light') input.hide_value = True - input = self.inputs.new('RendermanNodeSocketDisplacement', 'Displacement') + input = self.inputs.new('RendermanNodeSocketDisplacement', 'displace_in', identifier='Displacement') input.hide_value = True - input = self.inputs.new('RendermanNodeSocketLightFilter', 'LightFilter') + input = self.inputs.new('RendermanNodeSocketLightFilter', 'lightfilter_in', identifier='LightFilter') input.hide_value = True def draw_buttons(self, context, layout): @@ -741,7 +741,7 @@ class RendermanIntegratorsOutputNode(RendermanShadingNode): new_links = [] def init(self, context): - input = self.inputs.new('RendermanNodeSocketIntegrator', 'Integrator') + input = self.inputs.new('RendermanNodeSocketIntegrator', 'integrator_in', identifier='Integrator') def draw_buttons(self, context, layout): return @@ -781,11 +781,12 @@ class RendermanSamplefiltersOutputNode(RendermanShadingNode): new_links = [] def init(self, context): - input = self.inputs.new('RendermanNodeSocketSampleFilter', 'samplefilter[0]') + input = self.inputs.new('RendermanNodeSocketSampleFilter', 'samplefilter_in[0]', identifier='samplefilter[0]') input.hide_value = True def add_input(self): - input = self.inputs.new('RendermanNodeSocketSampleFilter', 'samplefilter[%d]' % (len(self.inputs))) + size = len(self.inputs) + input = self.inputs.new('RendermanNodeSocketSampleFilter', 'samplefilter_in[%d]' % size, identifier='samplefilter[%d]' % size) input.hide_value = True def remove_input(self): @@ -846,11 +847,12 @@ class RendermanDisplayfiltersOutputNode(RendermanShadingNode): new_links = [] def init(self, context): - input = self.inputs.new('RendermanNodeSocketDisplayFilter', 'displayfilter[0]') + input = self.inputs.new('RendermanNodeSocketDisplayFilter', 'displayfilter_in[0]', identifier='displayflter[0]') input.hide_value = True def add_input(self): - input = self.inputs.new('RendermanNodeSocketDisplayFilter', 'displayfilter[%d]' % (len(self.inputs))) + size = len(self.inputs) + input = self.inputs.new('RendermanNodeSocketDisplayFilter', 'displayfilter_in[%d]' % size, identifier='displayfilter[%d]' % size) input.hide_value = True def remove_input(self): @@ -914,7 +916,7 @@ def poll(cls, ntree): return ntree.bl_idname == 'ShaderNodeTree' def init(self, context): - input = self.inputs.new('RendermanNodeSocketProjection', 'Projection') + input = self.inputs.new('RendermanNodeSocketProjection', 'projection_in', identifier='Projection') def draw_buttons(self, context, layout): return diff --git a/rman_cycles_convert/__init__.py b/rman_cycles_convert/__init__.py index 9f422e36..cd0b0f4f 100644 --- a/rman_cycles_convert/__init__.py +++ b/rman_cycles_convert/__init__.py @@ -37,19 +37,19 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): elif not node1: rman_node2 = convert_cycles_bsdf(nt, rman_parent, node2, input_index) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], rman_parent.inputs['materialFront']) else: - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], rman_parent.inputs[input_index]) elif not node2: rman_node1 = convert_cycles_bsdf(nt, rman_parent, node1, input_index) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], rman_parent.inputs['materialFront']) else: - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], rman_parent.inputs[input_index]) elif node.bl_idname == 'ShaderNodeAddShader': @@ -57,10 +57,10 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): node_name = __BL_NODES_MAP__.get('LamaAdd') add = nt.nodes.new(node_name) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(add.outputs["Bxdf"], + nt.links.new(add.outputs["bxdf_out"], rman_parent.inputs['materialFront']) else: - nt.links.new(add.outputs["Bxdf"], + nt.links.new(add.outputs["bxdf_out"], rman_parent.inputs[input_index]) offset_node_location(rman_parent, add, node) @@ -68,9 +68,9 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): rman_node1 = convert_cycles_bsdf(nt, add, node1, 0) rman_node2 = convert_cycles_bsdf(nt, add, node2, 1) - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], add.inputs['material1']) - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], add.inputs['material2']) setattr(add, "weight1", 0.5) @@ -83,10 +83,10 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): node_name = __BL_NODES_MAP__.get('LamaMix') mixer = nt.nodes.new(node_name) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(mixer.outputs["Bxdf"], + nt.links.new(mixer.outputs["bxdf_out"], rman_parent.inputs['materialFront']) else: - nt.links.new(mixer.outputs["Bxdf"], + nt.links.new(mixer.outputs["bxdf_out"], rman_parent.inputs[input_index]) offset_node_location(rman_parent, mixer, node) @@ -97,9 +97,9 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): rman_node1 = convert_cycles_bsdf(nt, mixer, node1, 0) rman_node2 = convert_cycles_bsdf(nt, mixer, node2, 1) - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], mixer.inputs['material1']) - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], mixer.inputs['material2']) return mixer @@ -110,7 +110,7 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): convert_func(nt, node, rman_node) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(rman_node.outputs["Bxdf"], + nt.links.new(rman_node.outputs["bxdf_out"], rman_parent.inputs['materialFront']) if node.bl_idname == 'ShaderNodeBsdfTransparent': setattr(rman_parent, 'presence', 0.0) @@ -121,7 +121,7 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): node_name = __BL_NODES_MAP__.get('LamaDiffuse') rman_node = nt.nodes.new(node_name) if rman_parent.bl_label == 'LamaSurface': - nt.links.new(rman_node.outputs["Bxdf"], + nt.links.new(rman_node.outputs["bxdf_out"], rman_parent.inputs['materialFront']) return rman_node @@ -135,7 +135,7 @@ def convert_cycles_displacement(nt, displace_socket, output_node): node_name = __BL_NODES_MAP__.get('PxrDisplace') displace = nt.nodes.new(node_name) - nt.links.new(displace.outputs[0], output_node.inputs['Displacement']) + nt.links.new(displace.outputs[0], output_node.inputs['displace_in']) displace.location = output_node.location displace.location[0] -= 200 displace.location[1] -= 100 @@ -216,7 +216,7 @@ def convert_cycles_nodetree(id, output_node): if begin_cycles_node.bl_idname == "ShaderNodeEmission": node_name = __BL_NODES_MAP__.get('PxrMeshLight') meshlight = nt.nodes.new(node_name) - nt.links.new(meshlight.outputs[0], output_node.inputs["Light"]) + nt.links.new(meshlight.outputs[0], output_node.inputs["light_in"]) offset_node_location(output_node, meshlight, begin_cycles_node) convert_cycles_input(nt, begin_cycles_node.inputs[ 'Strength'], meshlight, "intensity") @@ -227,7 +227,7 @@ def convert_cycles_nodetree(id, output_node): setattr(meshlight, 'lightColor', begin_cycles_node.inputs[ 'Color'].default_value[:3]) bxdf = nt.nodes.new('PxrBlackBxdfNode') - nt.links.new(bxdf.outputs[0], output_node.inputs["Bxdf"]) + nt.links.new(bxdf.outputs[0], output_node.inputs["bxdf_in"]) else: if begin_cycles_node.bl_idname == "ShaderNodeBsdfPrincipled": # use PxrDisneyBsdf @@ -248,7 +248,7 @@ def convert_cycles_nodetree(id, output_node): rman_node = nt.nodes.new(node_name) convert_func(nt, begin_cycles_node, rman_node) - nt.links.new(rman_node.outputs['Bxdf'], base_surface.inputs["materialFront"]) + nt.links.new(rman_node.outputs['bxdf_out'], base_surface.inputs["materialFront"]) offset_node_location(output_node, base_surface, begin_cycles_node) rman_node.location = begin_cycles_node.location rman_node.location[0] -= 500 @@ -277,12 +277,12 @@ def convert_cycles_nodetree(id, output_node): node_name = __BL_NODES_MAP__.get(bxdf_name) rman_node = nt.nodes.new(node_name) convert_func(nt, begin_cycles_node, rman_node) - nt.links.new(rman_node.outputs[0], output_node.inputs["Bxdf"]) + nt.links.new(rman_node.outputs[0], output_node.inputs["bxdf_in"]) has_volume = True else: rfb_log().warning("Could not convert cycles volume node: %s" % begin_cycles_node.name) - elif cycles_output_node.inputs['Displacement'].is_linked: + if cycles_output_node.inputs['Displacement'].is_linked: convert_cycles_displacement(nt, cycles_output_node.inputs['Displacement'], output_node) has_displacement = True diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index 095999a4..c7aebd39 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -83,9 +83,9 @@ def convert_cycles_node(nt, node, location=None): rman_node1 = convert_cycles_bsdf(nt, add, node1, 0) rman_node2 = convert_cycles_bsdf(nt, add, node2, 1) - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], add.inputs['material1']) - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], add.inputs['material2']) setattr(add, "weight1", 0.5) @@ -106,9 +106,9 @@ def convert_cycles_node(nt, node, location=None): rman_node1 = convert_cycles_bsdf(nt, mixer, node1, 0) rman_node2 = convert_cycles_bsdf(nt, mixer, node2, 1) - nt.links.new(rman_node1.outputs["Bxdf"], + nt.links.new(rman_node1.outputs["bxdf_out"], mixer.inputs['material1']) - nt.links.new(rman_node2.outputs["Bxdf"], + nt.links.new(rman_node2.outputs["bxdf_out"], mixer.inputs['material2']) return mixer @@ -122,7 +122,7 @@ def convert_cycles_node(nt, node, location=None): rman_node = nt.nodes.new(node_name) return rman_node - node_name = __BL_NODES_MAP__.get(bxdf_name) + node_name = __BL_NODES_MAP__.get(rman_name) rman_node = nt.nodes.new(node_name) convert_func(nt, node, rman_node) converted_nodes[node.name] = rman_node.name @@ -470,8 +470,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): diff_sss_mix_node.name = 'mix_sss' nodes_list.append(diff_sss_mix_node) - nt.links.new(diffuse_node.outputs["Bxdf"], diff_sss_mix_node.inputs["material1"]) - nt.links.new(sss_node.outputs["Bxdf"], diff_sss_mix_node.inputs["material2"]) + nt.links.new(diffuse_node.outputs["bxdf_out"], diff_sss_mix_node.inputs["material1"]) + nt.links.new(sss_node.outputs["bxdf_out"], diff_sss_mix_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_sssMix"], diff_sss_mix_node.inputs["mix"]) # sheen @@ -488,8 +488,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): diff_sheen_add_node.name = 'plus_sheen' nodes_list.append(diff_sheen_add_node) - nt.links.new(diff_sss_mix_node.outputs["Bxdf"], diff_sheen_add_node.inputs["material1"]) - nt.links.new(sheen_node.outputs["Bxdf"], diff_sheen_add_node.inputs["material2"]) + nt.links.new(diff_sss_mix_node.outputs["bxdf_out"], diff_sheen_add_node.inputs["material1"]) + nt.links.new(sheen_node.outputs["bxdf_out"], diff_sheen_add_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_sheenWeight"], diff_sheen_add_node.inputs["weight2"]) # specular @@ -510,8 +510,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): sheen_spec_add_node.name = 'plus_spec' nodes_list.append(sheen_spec_add_node) - nt.links.new(diff_sheen_add_node.outputs["Bxdf"], sheen_spec_add_node.inputs["material1"]) - nt.links.new(specular_node.outputs["Bxdf"], sheen_spec_add_node.inputs["material2"]) + nt.links.new(diff_sheen_add_node.outputs["bxdf_out"], sheen_spec_add_node.inputs["material1"]) + nt.links.new(specular_node.outputs["bxdf_out"], sheen_spec_add_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_diffuseWeight"], sheen_spec_add_node.inputs["weight1"]) nt.links.new(rman_node.outputs["out_specularWeight"], sheen_spec_add_node.inputs["weight2"]) @@ -532,8 +532,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): spec_transmission_add_node.name = 'plus_transmission' nodes_list.append(spec_transmission_add_node) - nt.links.new(sheen_spec_add_node.outputs["Bxdf"], spec_transmission_add_node.inputs["material1"]) - nt.links.new(transmission_node.outputs["Bxdf"], spec_transmission_add_node.inputs["material2"]) + nt.links.new(sheen_spec_add_node.outputs["bxdf_out"], spec_transmission_add_node.inputs["material1"]) + nt.links.new(transmission_node.outputs["bxdf_out"], spec_transmission_add_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_finalTransmission"], spec_transmission_add_node.inputs["weight2"]) # coat @@ -551,8 +551,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): transmission_coat_add_node.name = 'plus_coat' nodes_list.append(transmission_coat_add_node) - nt.links.new(spec_transmission_add_node.outputs["Bxdf"], transmission_coat_add_node.inputs["material1"]) - nt.links.new(coat_node.outputs["Bxdf"], transmission_coat_add_node.inputs["material2"]) + nt.links.new(spec_transmission_add_node.outputs["bxdf_out"], transmission_coat_add_node.inputs["material1"]) + nt.links.new(coat_node.outputs["bxdf_out"], transmission_coat_add_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_clearcoat"], transmission_coat_add_node.inputs["weight2"]) # emission @@ -563,8 +563,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): nt.links.new(rman_node.outputs["out_emissionColor"], emission_node.inputs["color"]) # final mix node - nt.links.new(transmission_coat_add_node.outputs["Bxdf"], final_mix_node.inputs["material1"]) - nt.links.new(emission_node.outputs["Bxdf"], final_mix_node.inputs["material2"]) + nt.links.new(transmission_coat_add_node.outputs["bxdf_out"], final_mix_node.inputs["material1"]) + nt.links.new(emission_node.outputs["bxdf_out"], final_mix_node.inputs["material2"]) nt.links.new(rman_node.outputs["out_emissionMix"], final_mix_node.inputs["mix"]) # close ui_open connections diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index 6a61e696..8a025102 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -105,7 +105,7 @@ def execute(self, context): mat = shadergraph_utils.create_bxdf('PxrVolume') ob.active_material = mat output = shadergraph_utils.find_node(mat, 'RendermanOutputNode') - bxdf = output.inputs['Bxdf'].links[0].from_node + bxdf = output.inputs['bxdf_in'].links[0].from_node bxdf.densityFloatPrimVar = 'density' if self.properties.rman_open_filebrowser: diff --git a/rman_presets/core.py b/rman_presets/core.py index e959b52d..5236bcd8 100644 --- a/rman_presets/core.py +++ b/rman_presets/core.py @@ -578,26 +578,26 @@ def export_material_preset(mat, nodes_to_convert, renderman_output_node, Asset): nodeClass, rmanNode, externalosl=False) - if renderman_output_node.inputs['Bxdf'].is_linked: + if renderman_output_node.inputs['bxdf_in'].is_linked: infodict = {} infodict['name'] = 'rman__surface' infodict['type'] = 'reference float3' infodict['value'] = None Asset.addParam(nodeName, nodeType, 'rman__surface', infodict) - from_node = renderman_output_node.inputs['Bxdf'].links[0].from_node + from_node = renderman_output_node.inputs['bxdf_in'].links[0].from_node srcPlug = "%s.%s" % (fix_blender_name(from_node.name), 'outColor') dstPlug = "%s.%s" % (nodeName, 'rman__surface') Asset.addConnection(srcPlug, dstPlug) - if renderman_output_node.inputs['Displacement'].is_linked: + if renderman_output_node.inputs['displacement_input'].is_linked: infodict = {} infodict['name'] = 'rman__displacement' infodict['type'] = 'reference float3' infodict['value'] = None Asset.addParam(nodeName, nodeType, 'rman__displacement', infodict) - from_node = renderman_output_node.inputs['Displacement'].links[0].from_node + from_node = renderman_output_node.inputs['displacement_input'].links[0].from_node srcPlug = "%s.%s" % (fix_blender_name(from_node.name), 'outColor') dstPlug = "%s.%s" % (nodeName, 'rman__displacement') Asset.addConnection(srcPlug, dstPlug) @@ -1272,8 +1272,8 @@ def import_light_rig(Asset): def connectNodes(Asset, nt, nodeDict): output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanOutputNode') - bxdf_socket = output.inputs['Bxdf'] - displace_socket = output.inputs['Displacement'] + bxdf_socket = output.inputs['bxdf_in'] + displace_socket = output.inputs['displace_in'] for con in Asset.connectionList(): #print('+ %s.%s -> %s.%s' % (nodeDict[con.srcNode()](), con.srcParam(), @@ -1298,12 +1298,12 @@ def connectNodes(Asset, nt, nodeDict): elif output == dstNode: # check if this is a root node connection if dstSocket == 'surfaceShader' or dstSocket == 'rman__surface': - nt.links.new(srcNode.outputs['Bxdf'], output.inputs['Bxdf']) + nt.links.new(srcNode.outputs['bxdf_out'], output.inputs['bxdf_in']) elif dstSocket == 'displacementShader' or dstSocket == 'rman__displacement': - nt.links.new(srcNode.outputs['Displacement'], output.inputs['Displacement']) + nt.links.new(srcNode.outputs['displace_out'], output.inputs['displace_in']) elif renderman_node_type == 'bxdf': # this is a regular upstream bxdf connection - nt.links.new(srcNode.outputs['Bxdf'], dstNode.inputs[dstSocket]) + nt.links.new(srcNode.outputs['bxdf_out'], dstNode.inputs[dstSocket]) else: rfb_log().debug('error connecting %s.%s to %s.%s' % (srcNode.name,srcSocket, dstNode.name, dstSocket)) @@ -1315,16 +1315,16 @@ def connectNodes(Asset, nt, nodeDict): for node in nt.nodes: renderman_node_type = getattr(node, 'renderman_node_type', '') if renderman_node_type == 'bxdf': - if not node.outputs['Bxdf'].is_linked: + if not node.outputs['bxdf_out'].is_linked: bxdf_candidate = node elif renderman_node_type == 'displace': displace_candidate = node if bxdf_candidate: - nt.links.new(bxdf_candidate.outputs['Bxdf'], output.inputs['Bxdf']) + nt.links.new(bxdf_candidate.outputs['bxdf_out'], output.inputs['bxdf_in']) if not displace_socket.is_linked and displace_candidate: - nt.links.new(displace_candidate.outputs['Displacement'], output.inputs['Displacement']) + nt.links.new(displace_candidate.outputs['displace_out'], output.inputs['displace_in']) def create_displayfilter_nodes(Asset): @@ -1350,7 +1350,7 @@ def create_displayfilter_nodes(Asset): created_node.name = node_id created_node.label = node_id output.add_input() - nt.links.new(created_node.outputs['DisplayFilter'], output.inputs[-1]) + nt.links.new(created_node.outputs['displayfilter_out'], output.inputs[-1]) nodeDict[node_id] = created_node.name setParams(Asset, created_node, df_node.paramsDict()) diff --git a/rman_translators/rman_material_translator.py b/rman_translators/rman_material_translator.py index 3e19adcf..6496efe8 100644 --- a/rman_translators/rman_material_translator.py +++ b/rman_translators/rman_material_translator.py @@ -115,7 +115,7 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): return True # bxdf - socket = out.inputs['Bxdf'] + socket = out.inputs['bxdf_in'] if socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='bxdf') if linked_node: @@ -139,7 +139,7 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): self.create_pxrdiffuse_node(rman_sg_material, handle) # light - socket = out.inputs['Light'] + socket = out.inputs['light_in'] if socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='light') if linked_node: @@ -158,7 +158,7 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): rman_sg_material.sg_node.SetLight(lightNodesList) # displacement - socket = out.inputs['Displacement'] + socket = out.inputs['displace_in'] if socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='displace') if linked_node: diff --git a/rman_ui/rman_ui_camera_panels.py b/rman_ui/rman_ui_camera_panels.py index 8dd30da1..c316fe6a 100644 --- a/rman_ui/rman_ui_camera_panels.py +++ b/rman_ui/rman_ui_camera_panels.py @@ -55,7 +55,7 @@ def draw(self, context): split = layout.split(factor=0.35) - split.label(text=socket.name + ':') + split.label(text=socket.identifier + ':') split.context_pointer_set("socket", socket) split.context_pointer_set("node", output) diff --git a/rman_ui/rman_ui_material_panels.py b/rman_ui/rman_ui_material_panels.py index 91c221ff..95ee3254 100644 --- a/rman_ui/rman_ui_material_panels.py +++ b/rman_ui/rman_ui_material_panels.py @@ -159,14 +159,15 @@ def draw(self, context): col = split.column() layout.separator() - if not rman_output_node.inputs['Bxdf'].is_linked: + input_name = 'bxdf_in' + if not rman_output_node.inputs[input_name].is_linked: panel_node_draw(layout, context, mat, 'RendermanOutputNode', 'Bxdf') elif not filter_parameters or filter_method == 'NONE': panel_node_draw(layout, context, mat, 'RendermanOutputNode', 'Bxdf') elif filter_method == 'STICKY': - bxdf_node = rman_output_node.inputs['Bxdf'].links[0].from_node + bxdf_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(bxdf_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -175,7 +176,7 @@ def draw(self, context): expr = rman_output_node.bxdf_match_expression if expr == '': return - bxdf_node = rman_output_node.inputs['Bxdf'].links[0].from_node + bxdf_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(bxdf_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -271,14 +272,15 @@ def draw(self, context): col = split.column() col = split.column() - if not rman_output_node.inputs['Light'].is_linked: + input_name = 'light_in' + if not rman_output_node.inputs[input_name].is_linked: draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) elif not filter_parameters or filter_method == 'NONE': draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) elif filter_method == 'STICKY': - light_node = rman_output_node.inputs['Light'].links[0].from_node + light_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(light_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -287,7 +289,7 @@ def draw(self, context): expr = rman_output_node.light_match_expression if expr == '': return - light_node = rman_output_node.inputs['Light'].links[0].from_node + light_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(light_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -295,7 +297,7 @@ def draw(self, context): prop_names, context, nt) else: draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) class MATERIAL_PT_renderman_shader_displacement(ShaderPanel, Panel): bl_context = "material" @@ -336,14 +338,15 @@ def draw(self, context): col = split.column() col = split.column() - if not rman_output_node.inputs['Displacement'].is_linked: + input_name = 'displace_in' + if not rman_output_node.inputs[input_name].is_linked: draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) elif not filter_parameters or filter_method == 'NONE': draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) elif filter_method == 'STICKY': - disp_node = rman_output_node.inputs['Displacement'].links[0].from_node + disp_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(disp_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -352,7 +355,7 @@ def draw(self, context): expr = rman_output_node.disp_match_expression if expr == '': return - disp_node = rman_output_node.inputs['Displacement'].links[0].from_node + disp_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(disp_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -360,7 +363,7 @@ def draw(self, context): prop_names, context, nt) else: draw_nodes_properties_ui( - layout, context, nt, input_name=self.shader_type) + layout, context, nt, input_name=input_name) class DATA_PT_renderman_light(ShaderPanel, Panel): bl_context = "data" @@ -474,7 +477,7 @@ def draw(self, context): if light.node_tree: nt = light.node_tree draw_nodes_properties_ui( - self.layout, context, nt, input_name='Light') + self.layout, context, nt, input_name='light_in') class DATA_PT_renderman_node_shader_lightfilter(ShaderNodePanel, Panel): bl_label = "Light Filter Parameters" @@ -494,7 +497,7 @@ def draw(self, context): if light.node_tree: nt = light.node_tree draw_nodes_properties_ui( - self.layout, context, nt, input_name='LightFilter') + self.layout, context, nt, input_name='lightfilter_in') class RENDERMAN_UL_LightFilters(CollectionPanel): def draw_item(self, layout, context, item): @@ -518,7 +521,7 @@ def draw_item(self, layout, context, item): nt = lightfilter.data.node_tree draw_nodes_properties_ui( - self.layout, context, nt, input_name='LightFilter') + self.layout, context, nt, input_name='lightfilter_in') else: layout.label(text='No light filter linked') @@ -557,7 +560,7 @@ def draw_item(self, layout, context, item): if portal.data.node_tree: nt = portal.data.node_tree draw_nodes_properties_ui( - self.layout, context, nt, input_name='Light') + self.layout, context, nt, input_name='lightfilter_in') else: layout.label(text='No portal light linked') diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index 79ec5687..dd2e32e6 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -233,14 +233,15 @@ def draw(self, context): col = split.column() layout.separator() - if not rman_output_node.inputs['Bxdf'].is_linked: + input_name = 'bxdf_input' + if not rman_output_node.inputs[input_name].is_linked: panel_node_draw(layout, context, mat, 'RendermanOutputNode', 'Bxdf') elif not filter_parameters or filter_method == 'NONE': panel_node_draw(layout, context, mat, 'RendermanOutputNode', 'Bxdf') elif filter_method == 'STICKY': - bxdf_node = rman_output_node.inputs['Bxdf'].links[0].from_node + bxdf_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(bxdf_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -249,7 +250,7 @@ def draw(self, context): expr = rman_output_node.bxdf_match_expression if expr == '': return - bxdf_node = rman_output_node.inputs['Bxdf'].links[0].from_node + bxdf_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(bxdf_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -339,14 +340,15 @@ def draw(self, context): col = split.column() shader_type = 'Displacement' - if not rman_output_node.inputs['Displacement'].is_linked: + input_name = 'displacement_input' + if not rman_output_node.inputs[input_name].is_linked: draw_nodes_properties_ui( - layout, context, nt, input_name=shader_type) + layout, context, nt, input_name=input_name) elif not filter_parameters or filter_method == 'NONE': draw_nodes_properties_ui( - layout, context, nt, input_name=shader_type) + layout, context, nt, input_name=input_name) elif filter_method == 'STICKY': - disp_node = rman_output_node.inputs['Displacement'].links[0].from_node + disp_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(disp_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -355,7 +357,7 @@ def draw(self, context): expr = rman_output_node.disp_match_expression if expr == '': return - disp_node = rman_output_node.inputs['Displacement'].links[0].from_node + disp_node = rman_output_node.inputs[input_name].links[0].from_node nodes = gather_nodes(disp_node) for node in nodes: prop_names = getattr(node, 'prop_names', list()) @@ -363,7 +365,7 @@ def draw(self, context): prop_names, context, nt) else: draw_nodes_properties_ui( - layout, context, nt, input_name=shader_type) + layout, context, nt, input_name=input_name) class OBJECT_PT_renderman_object_geometry_quadric(Panel, CollectionPanel): bl_space_type = 'PROPERTIES' diff --git a/rman_ui/rman_ui_render_panels.py b/rman_ui/rman_ui_render_panels.py index cebda51e..99e6b67d 100644 --- a/rman_ui/rman_ui_render_panels.py +++ b/rman_ui/rman_ui_render_panels.py @@ -131,7 +131,7 @@ def draw(self, context): rm = world.renderman nt = world.node_tree - draw_nodes_properties_ui(layout, context, nt, input_name='Integrator', output_node_type='integrators_output') + draw_nodes_properties_ui(layout, context, nt, input_name='integrator_in', output_node_type='integrators_output') class RENDER_PT_renderman_world_display_filters(PRManButtonsPanel, Panel): bl_label = "Display Filters" @@ -174,7 +174,7 @@ def draw(self, context): op = col.operator("node.rman_remove_displayfilter_node_socket", text="", icon="REMOVE") op.index = i col = row.column() - col.label(text=socket.name) + col.label(text=socket.identifier) if socket.is_linked: col = row.column() @@ -247,7 +247,7 @@ def draw(self, context): op = col.operator("node.rman_remove_samplefilter_node_socket", text="", icon="REMOVE") op.index = i col = row.column() - col.label(text=socket.name) + col.label(text=socket.identifier) if socket.is_linked: col = row.column() diff --git a/rman_ui/rman_ui_world_panels.py b/rman_ui/rman_ui_world_panels.py index 4e610783..cdd6e088 100644 --- a/rman_ui/rman_ui_world_panels.py +++ b/rman_ui/rman_ui_world_panels.py @@ -57,7 +57,7 @@ def draw(self, context): rm = world.renderman nt = world.node_tree - draw_nodes_properties_ui(layout, context, nt, input_name='Integrator', output_node_type='integrators_output') + draw_nodes_properties_ui(layout, context, nt, input_name='integrator_in', output_node_type='integrators_output') class DATA_PT_renderman_world_display_filters(ShaderPanel, Panel): bl_label = "Display Filters" From eff6ac1d2fa8ec5d399bdbab562538462df9e1d9 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 12 May 2022 12:04:17 -0700 Subject: [PATCH 116/278] ice now supports creating images from numpy arrays. Switch to using ice to create the AOVs for blender_batch and for saving the viewport snapshots. --- rman_render.py | 92 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/rman_render.py b/rman_render.py index 9304f57f..9359eed8 100644 --- a/rman_render.py +++ b/rman_render.py @@ -1,6 +1,7 @@ import time import os import rman +import ice import bpy import sys from .rman_constants import RFB_VIEWPORT_MAX_BUCKETS, RMAN_RENDERMAN_BLUE @@ -329,31 +330,42 @@ def finish_passes(self): # check if we should write out the AOVs if self.write_aovs: + use_ice = hasattr(ice, 'FromArray') for i, dspy_nm in enumerate(self.dspy_dict['displays'].keys()): filepath = self.dspy_dict['displays'][dspy_nm]['filePath'] - buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, as_flat=True) - if buffer is None: - continue if i == 0: # write out the beauty with a 'raw' substring toks = os.path.splitext(filepath) filepath = '%s_beauty_raw.exr' % (toks[0]) - - bl_image = bpy.data.images.new(dspy_nm, self.width, self.height) - try: - if isinstance(buffer, numpy.ndarray): - buffer = buffer.tolist() - bl_image.use_generated_float = True - bl_image.filepath_raw = filepath - bl_image.pixels.foreach_set(buffer) - bl_image.file_format = 'OPEN_EXR' - bl_image.update() - bl_image.save() - except: - pass - finally: - bpy.data.images.remove(bl_image) + + if use_ice: + buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, raw_buffer=True, as_flat=True) + if buffer is None: + continue + + # use ice to save out the image + img = ice.FromArray(buffer) + img = img.Flip(False, True, False) + img.Save(filepath, ice.constants.FMT_EXRFLOAT) + else: + buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, as_flat=True) + if buffer is None: + continue + bl_image = bpy.data.images.new(dspy_nm, self.width, self.height) + try: + if isinstance(buffer, numpy.ndarray): + buffer = buffer.tolist() + bl_image.use_generated_float = True + bl_image.filepath_raw = filepath + bl_image.pixels.foreach_set(buffer) + bl_image.file_format = 'OPEN_EXR' + bl_image.update() + bl_image.save() + except: + pass + finally: + bpy.data.images.remove(bl_image) class RmanRender(object): ''' @@ -1339,7 +1351,7 @@ def get_numchannels(self, image_num): num_channels = dspy_plugin.GetNumberOfChannels(ctypes.c_size_t(image_num)) return num_channels - def _get_buffer(self, width, height, image_num=0, num_channels=-1, back_fill=True, as_flat=True, render=None): + def _get_buffer(self, width, height, image_num=0, num_channels=-1, raw_buffer=False, back_fill=True, as_flat=True, render=None): dspy_plugin = self.get_blender_dspy_plugin() if num_channels == -1: num_channels = self.get_numchannels(image_num) @@ -1352,7 +1364,12 @@ def _get_buffer(self, width, height, image_num=0, num_channels=-1, back_fill=Tru f.restype = ctypes.POINTER(ArrayType) try: - buffer = numpy.array(f(ctypes.c_size_t(image_num)).contents) + buffer = numpy.array(f(ctypes.c_size_t(image_num)).contents, dtype=numpy.float32) + + if raw_buffer: + if not as_flat: + buffer.shape = (height, width, num_channels) + return buffer if as_flat: if (num_channels == 4) or not back_fill: @@ -1436,18 +1453,31 @@ def save_viewport_snapshot(self, frame=1): width = int(self.viewport_res_x * res_mult) height = int(self.viewport_res_y * res_mult) - pixels = self._get_buffer(width, height) - if pixels is None: - rfb_log().error("Could not save snapshot.") - return - - nm = 'rman_viewport_snapshot__%d' % len(bpy.data.images) + nm = 'rman_viewport_snapshot__%d.exr' % len(bpy.data.images) nm = string_utils.expand_string(nm, frame=frame) - img = bpy.data.images.new(nm, width, height, float_buffer=True, alpha=True) - if isinstance(pixels, numpy.ndarray): - pixels = pixels.tolist() - img.pixels.foreach_set(pixels) - img.update() + if hasattr(ice, 'FromArray'): + buffer = self._get_buffer(width, height, as_flat=False, raw_buffer=True) + if buffer is None: + rfb_log().error("Could not save snapshot.") + return + img = ice.FromArray(buffer) + img = img.Flip(False, True, False) + filepath = os.path.join(bpy.app.tempdir, nm) + img.Save(filepath, ice.constants.FMT_EXRFLOAT) + bpy.ops.image.open('EXEC_DEFAULT', filepath=filepath) + bpy.data.images[-1].pack() + os.remove(filepath) + else: + buffer = self._get_buffer(width, height) + if buffer is None: + rfb_log().error("Could not save snapshot.") + return + + img = bpy.data.images.new(nm, width, height, float_buffer=True, alpha=True) + if isinstance(buffer, numpy.ndarray): + buffer = buffer.tolist() + img.pixels.foreach_set(buffer) + img.update() def update_scene(self, context, depsgraph): if self.rman_interactive_running: From 454638092126e6ebfdcc101461eaa2f00ed04949 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 12 May 2022 12:07:58 -0700 Subject: [PATCH 117/278] Add some extra error checking in the upgrade_250 function. --- rfb_utils/upgrade_utils.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index d2f8b91e..e71eab5c 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -41,7 +41,8 @@ def upgrade_250(scene): if old_name in node.outputs: node.outputs[old_name].name = '%s_out' % renderman_node_type elif renderman_node_type in ['displace', 'displacement']: - node.outputs['Displacement'].name = 'displace_out' + if 'Displacement' in node.outputs: + node.outputs['Displacement'].name = 'displace_out' elif renderman_node_type == 'lightfilter': if 'LightFilter' in node.outputs: node.outputs['LightFilter'].name = '%s_out' % renderman_node_type @@ -55,21 +56,28 @@ def upgrade_250(scene): for mat in bpy.data.materials: output = shadergraph_utils.find_node(mat, 'RendermanOutputNode') if output: - output.inputs['Bxdf'].name = 'bxdf_in' - output.inputs['Light'].name = 'light_in' - output.inputs['Displacement'].name = 'displace_in' - output.inputs['LightFilter'].name = 'lightfilter_in' + if 'Bxdf' in output.inputs: + output.inputs['Bxdf'].name = 'bxdf_in' + if 'Light' in output.inputs: + output.inputs['Light'].name = 'light_in' + if 'Displacement' in output.inputs: + output.inputs['Displacement'].name = 'displace_in' + if 'LightFilter' in output.inputs: + output.inputs['LightFilter'].name = 'lightfilter_in' for light in bpy.data.lights: output = shadergraph_utils.is_renderman_nodetree(light) if output: - output.inputs['Light'].name = 'light_in' - output.inputs['LightFilter'].name = 'lightfilter_in' + if 'Light' in output.inputs: + output.inputs['Light'].name = 'light_in' + if 'LightFilter' in output.inputs: + output.inputs['LightFilter'].name = 'lightfilter_in' for world in bpy.data.worlds: output = shadergraph_utils.find_node(world, 'RendermanIntegratorsOutputNode') if output: - output.inputs['Integrator'].name = 'integrator_in' + if 'Integrator' in output.inputs: + output.inputs['Integrator'].name = 'integrator_in' output = shadergraph_utils.find_node(world, 'RendermanSamplefiltersOutputNode') if output: @@ -85,7 +93,8 @@ def upgrade_250(scene): nt = camera.renderman.rman_nodetree if nt: output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanProjectionsOutputNode') - output.inputs['Projection'].name = 'projection_in' + if 'Projection' in output.inputs: + output.inputs['Projection'].name = 'projection_in' __RMAN_SCENE_UPGRADE_FUNCTIONS__ = OrderedDict() From 13fd6fdd30bd1bd842cd59bc6373448267c75f9f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 13 May 2022 11:39:10 -0700 Subject: [PATCH 118/278] Remove our addon preferences operator, and use Blender's own preferences.addon_show operator. --- rman_operators/rman_operators_utils.py | 20 +------------------- rman_ui/rman_ui_view3d_panels.py | 3 ++- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/rman_operators/rman_operators_utils.py b/rman_operators/rman_operators_utils.py index 2339381f..a582558b 100644 --- a/rman_operators/rman_operators_utils.py +++ b/rman_operators/rman_operators_utils.py @@ -248,27 +248,9 @@ def invoke(self, context, event): return {'RUNNING_MODAL'} -class PRMAN_OT_Renderman_Open_Addon_Preferences(bpy.types.Operator): - bl_idname = "renderman.open_addon_preferences" - bl_label = "Addon Preferences" - bl_description = "Open the RenderMan for Blender addon prferences" - bl_options = {'INTERNAL'} - - - @classmethod - def poll(cls, context): - return context.engine == "PRMAN_RENDER" - - def execute(self, context): - context.preferences.active_section = 'ADDONS' - context.window_manager.addon_search = 'RenderMan For Blender' - bpy.ops.screen.userpref_show() - return {"FINISHED"} - classes = [ PRMAN_OT_Renderman_Package, - PRMAN_OT_Renderman_Start_Debug_Server, - PRMAN_OT_Renderman_Open_Addon_Preferences + PRMAN_OT_Renderman_Start_Debug_Server ] def register(): diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index 27c20e1b..1bf2b6f6 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -237,7 +237,8 @@ def draw(self, context): layout.label(text='Utilities:') box = layout.box() rman_addon_prefs = rfb_icons.get_icon('rman_loadplugin') - box.operator("renderman.open_addon_preferences", icon_value=rman_addon_prefs.icon_id) + op = box.operator("preferences.addon_show", text="Addon Preferences", icon_value=rman_addon_prefs.icon_id) + op.module = "RenderManForBlender" rman_pack_scene = rfb_icons.get_icon('rman_package_scene') box.operator("renderman.scene_package", icon_value=rman_pack_scene.icon_id) From f926c0498da6f464c351f08eb4cf0b1754277d23 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 13 May 2022 12:50:06 -0700 Subject: [PATCH 119/278] Fix bug where the viewport progress bar color would not show when bucket markers were turned off. * bind() needs to be called before we set the color. --- rman_render.py | 133 +++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/rman_render.py b/rman_render.py index 9359eed8..33ea9621 100644 --- a/rman_render.py +++ b/rman_render.py @@ -63,7 +63,7 @@ def __draw_callback__(): return False DRAWCALLBACK_FUNC = ctypes.CFUNCTYPE(ctypes.c_bool) -__CALLBACK_FUNC__ = DRAWCALLBACK_FUNC(__draw_callback__) +__CALLBACK_FUNC__ = DRAWCALLBACK_FUNC(__draw_callback__) class ItHandler(chatserver.ItBaseHandler): @@ -1280,71 +1280,72 @@ def reset_buffer_updated(self): def draw_pixels(self, width, height): self.viewport_res_x = width self.viewport_res_y = height - if self.rman_is_viewport_rendering: - dspy_plugin = self.get_blender_dspy_plugin() - - # (the driver will handle pixel scaling to the given viewport size) - dspy_plugin.DrawBufferToBlender(ctypes.c_int(width), ctypes.c_int(height)) - - if self.do_draw_buckets(): - # draw bucket indicator - image_num = 0 - arXMin = ctypes.c_int(0) - arXMax = ctypes.c_int(0) - arYMin = ctypes.c_int(0) - arYMax = ctypes.c_int(0) - dspy_plugin.GetActiveRegion(ctypes.c_size_t(image_num), ctypes.byref(arXMin), ctypes.byref(arXMax), ctypes.byref(arYMin), ctypes.byref(arYMax)) - if ( (arXMin.value + arXMax.value + arYMin.value + arYMax.value) > 0): - yMin = height-1 - arYMin.value - yMax = height-1 - arYMax.value - xMin = arXMin.value - xMax = arXMax.value - if self.rman_scene.viewport_render_res_mult != 1.0: - # render resolution multiplier is set, we need to re-scale the bucket markers - scaled_width = width * self.rman_scene.viewport_render_res_mult - xMin = int(width * ((arXMin.value) / (scaled_width))) - xMax = int(width * ((arXMax.value) / (scaled_width))) - - scaled_height = height * self.rman_scene.viewport_render_res_mult - yMin = height-1 - int(height * ((arYMin.value) / (scaled_height))) - yMax = height-1 - int(height * ((arYMax.value) / (scaled_height))) - - vertices = [] - c1 = (xMin, yMin) - c2 = (xMax, yMin) - c3 = (xMax, yMax) - c4 = (xMin, yMax) - vertices.append(c1) - vertices.append(c2) - vertices.append(c3) - vertices.append(c4) - indices = [(0, 1), (1, 2), (2,3), (3, 0)] - - # we've reach our max buckets, pop the oldest one off the list - if len(self.viewport_buckets) > RFB_VIEWPORT_MAX_BUCKETS: - self.viewport_buckets.pop() - self.viewport_buckets.insert(0,[vertices, indices]) - - bucket_color = get_pref('rman_viewport_bucket_color', default=RMAN_RENDERMAN_BLUE) - - # draw from newest to oldest - for v, i in (self.viewport_buckets): - shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') - shader.uniform_float("color", bucket_color) - batch = batch_for_shader(shader, 'LINES', {"pos": v}, indices=i) - shader.bind() - batch.draw(shader) - - # draw progress bar at the bottom of the viewport - if self.do_draw_progressbar(): - progress = self.stats_mgr._progress / 100.0 - progress_color = get_pref('rman_viewport_progress_color', default=RMAN_RENDERMAN_BLUE) - shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') - shader.uniform_float("color", progress_color) - vtx = [(0, 1), (width * progress, 1)] - batch = batch_for_shader(shader, 'LINES', {"pos": vtx}) - shader.bind() - batch.draw(shader) + if self.rman_is_viewport_rendering is False: + return + + dspy_plugin = self.get_blender_dspy_plugin() + + # (the driver will handle pixel scaling to the given viewport size) + dspy_plugin.DrawBufferToBlender(ctypes.c_int(width), ctypes.c_int(height)) + if self.do_draw_buckets(): + # draw bucket indicator + image_num = 0 + arXMin = ctypes.c_int(0) + arXMax = ctypes.c_int(0) + arYMin = ctypes.c_int(0) + arYMax = ctypes.c_int(0) + dspy_plugin.GetActiveRegion(ctypes.c_size_t(image_num), ctypes.byref(arXMin), ctypes.byref(arXMax), ctypes.byref(arYMin), ctypes.byref(arYMax)) + if ( (arXMin.value + arXMax.value + arYMin.value + arYMax.value) > 0): + yMin = height-1 - arYMin.value + yMax = height-1 - arYMax.value + xMin = arXMin.value + xMax = arXMax.value + if self.rman_scene.viewport_render_res_mult != 1.0: + # render resolution multiplier is set, we need to re-scale the bucket markers + scaled_width = width * self.rman_scene.viewport_render_res_mult + xMin = int(width * ((arXMin.value) / (scaled_width))) + xMax = int(width * ((arXMax.value) / (scaled_width))) + + scaled_height = height * self.rman_scene.viewport_render_res_mult + yMin = height-1 - int(height * ((arYMin.value) / (scaled_height))) + yMax = height-1 - int(height * ((arYMax.value) / (scaled_height))) + + vertices = [] + c1 = (xMin, yMin) + c2 = (xMax, yMin) + c3 = (xMax, yMax) + c4 = (xMin, yMax) + vertices.append(c1) + vertices.append(c2) + vertices.append(c3) + vertices.append(c4) + indices = [(0, 1), (1, 2), (2,3), (3, 0)] + + # we've reach our max buckets, pop the oldest one off the list + if len(self.viewport_buckets) > RFB_VIEWPORT_MAX_BUCKETS: + self.viewport_buckets.pop() + self.viewport_buckets.insert(0,[vertices, indices]) + + bucket_color = get_pref('rman_viewport_bucket_color', default=RMAN_RENDERMAN_BLUE) + + # draw from newest to oldest + shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') + shader.bind() + shader.uniform_float("color", bucket_color) + for v, i in (self.viewport_buckets): + batch = batch_for_shader(shader, 'LINES', {"pos": v}, indices=i) + batch.draw(shader) + + # draw progress bar at the bottom of the viewport + if self.do_draw_progressbar(): + progress = self.stats_mgr._progress / 100.0 + shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') + shader.bind() + progress_color = get_pref('rman_viewport_progress_color', default=RMAN_RENDERMAN_BLUE) + shader.uniform_float("color", progress_color) + vtx = [(0, 1), (width * progress, 1)] + batch = batch_for_shader(shader, 'LINES', {"pos": vtx}) + batch.draw(shader) def get_numchannels(self, image_num): dspy_plugin = self.get_blender_dspy_plugin() From 77b9e635173744a5d5b5db4e71151ffd9f357780 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 13 May 2022 13:45:58 -0700 Subject: [PATCH 120/278] * modify rman_constants to use sys.version_info to get the python version used by Blender * modify env_config to print an error if the python version being used is not supported. --- rfb_utils/envconfig_utils.py | 18 ++++++++++++++---- rman_constants.py | 10 +++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rfb_utils/envconfig_utils.py b/rfb_utils/envconfig_utils.py index e57012ef..d5016ce4 100644 --- a/rfb_utils/envconfig_utils.py +++ b/rfb_utils/envconfig_utils.py @@ -75,7 +75,6 @@ def config_environment(self): self._set_it_path() self._set_localqueue_path() self._set_license_app_path() - self._config_pythonpath() self._set_ocio() self._get_license_info() @@ -139,17 +138,21 @@ def get_shader_registration_paths(self): return paths - def _config_pythonpath(self): + def config_pythonpath(self): python_vers = 'python%s' % rman_constants.BLENDER_PYTHON_VERSION rfb_log().debug("Blender Python Version: %s" % rman_constants.BLENDER_PYTHON_VERSION) if platform.system() == 'Windows': rman_packages = os.path.join(self.rmantree, 'lib', python_vers, 'Lib', 'site-packages') else: rman_packages = os.path.join(self.rmantree, 'lib', python_vers, 'site-packages') + if not os.path.exists(rman_packages): + return False + sys.path.append(rman_packages) sys.path.append(os.path.join(self.rmantree, 'bin')) pythonbindings = os.path.join(self.rmantree, 'bin', 'pythonbindings') - sys.path.append(pythonbindings) + sys.path.append(pythonbindings) + return True def _append_to_path(self, path): if path is not None: @@ -385,13 +388,20 @@ def _guess_rmantree(): if buildinfo._version_major == rman_constants.RMAN_SUPPORTED_VERSION_MAJOR and buildinfo._version_minor < rman_constants.RMAN_SUPPORTED_VERSION_MINOR: rfb_log().error("Error loading addon using RMANTREE=%s. The minor version found (%s) is not supported. Minimum version supported is %s." % (rmantree, buildinfo._version_minor, rman_constants.RMAN_SUPPORTED_VERSION_STRING)) __RMAN_ENV_CONFIG__ = None - return None + return None rfb_log().debug("Guessed RMANTREE: %s" % rmantree) # Create an RmanEnvConfig object __RMAN_ENV_CONFIG__.rmantree = rmantree __RMAN_ENV_CONFIG__.build_info = buildinfo + + # configure python path + if not __RMAN_ENV_CONFIG__.config_pythonpath(): + rfb_log().error("The Python version this Blender uses (%s) is not supported by this version of RenderMan (%s)" % (rman_constants.BLENDER_PYTHON_VERSION, rman_constants.RMAN_SUPPORTED_VERSION_STRING)) + __RMAN_ENV_CONFIG__ = None + return None + __RMAN_ENV_CONFIG__.config_environment() return __RMAN_ENV_CONFIG__ diff --git a/rman_constants.py b/rman_constants.py index fad51f2b..ae10fb77 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -1,5 +1,6 @@ import os import bpy +import sys RFB_ADDON_VERSION_MAJOR = 24 RFB_ADDON_VERSION_MINOR = 4 @@ -19,11 +20,10 @@ BLENDER_SUPPORTED_VERSION_PATCH = 0 BLENDER_SUPPORTED_VERSION = (BLENDER_SUPPORTED_VERSION_MAJOR, BLENDER_SUPPORTED_VERSION_MINOR, BLENDER_SUPPORTED_VERSION_PATCH) -BLENDER_PYTHON_VERSION = "3.7" -if BLENDER_VERSION_MAJOR > 2: - BLENDER_PYTHON_VERSION = "3.9" -elif BLENDER_VERSION_MAJOR == 2 and BLENDER_VERSION_MINOR >= 93: - BLENDER_PYTHON_VERSION = "3.9" +pyver = sys.version_info +BLENDER_PYTHON_VERSION_MAJOR = pyver.major +BLENDER_PYTHON_VERSION_MINOR = pyver.minor +BLENDER_PYTHON_VERSION = '%s.%s' % (pyver.major, pyver.minor) RMAN_SUPPORTED_VERSION_MAJOR = 24 RMAN_SUPPORTED_VERSION_MINOR = 4 From f4be7a57297d2fe9c6fb835667cc499c2429257e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 17 May 2022 12:05:42 -0700 Subject: [PATCH 121/278] Try to hide all cycles nodes when we create materials, lights etc. --- rfb_utils/shadergraph_utils.py | 18 +++++++++++++++++- rman_cycles_convert/__init__.py | 7 ++++++- rman_cycles_convert/cycles_convert.py | 4 ++++ rman_operators/rman_operators_nodetree.py | 14 ++++++++++---- rman_operators/rman_operators_view3d.py | 4 +++- rman_presets/core.py | 1 + 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/rfb_utils/shadergraph_utils.py b/rfb_utils/shadergraph_utils.py index 895674ed..a9be0887 100644 --- a/rfb_utils/shadergraph_utils.py +++ b/rfb_utils/shadergraph_utils.py @@ -839,11 +839,27 @@ def has_stylized_pattern_node(ob, node=None): return False +def hide_cycles_nodes(id): + cycles_output_node = None + if isinstance(id, bpy.types.Material): + cycles_output_node = find_node(id, 'ShaderNodeOutputMaterial') + elif isinstance(id, bpy.types.Light): + cycles_output_node = find_node(id, 'ShaderNodeOutputLight') + elif isinstance(id, bpy.types.World): + cycles_output_node = find_node(id, 'ShaderNodeOutputWorld') + if not cycles_output_node: + return + cycles_output_node.hide = True + for i in cycles_output_node.inputs: + if i.is_linked: + i.links[0].from_node.hide = True + + def create_bxdf(bxdf): mat = bpy.data.materials.new(bxdf) - mat.use_nodes = True nt = mat.node_tree + hide_cycles_nodes(mat) output = nt.nodes.new('RendermanOutputNode') default = nt.nodes.new('%sBxdfNode' % bxdf) diff --git a/rman_cycles_convert/__init__.py b/rman_cycles_convert/__init__.py index 07c5e9c7..7b14382b 100644 --- a/rman_cycles_convert/__init__.py +++ b/rman_cycles_convert/__init__.py @@ -53,7 +53,8 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): rman_parent.inputs[input_index]) elif node.bl_idname == 'ShaderNodeAddShader': - + node1.hide = True + node2.hide = True node_name = __BL_NODES_MAP__.get('LamaAdd') add = nt.nodes.new(node_name) if rman_parent.bl_label == 'LamaSurface': @@ -79,6 +80,8 @@ def convert_cycles_bsdf(nt, rman_parent, node, input_index): return add elif node.bl_idname == 'ShaderNodeMixShader': + node1.hide = True + node2.hide = True node_name = __BL_NODES_MAP__.get('LamaMix') mixer = nt.nodes.new(node_name) @@ -273,6 +276,8 @@ def convert_cycles_nodetree(id, output_node): base_surface.update_mat(id) has_bxdf = True elif cycles_output_node.inputs['Volume'].is_linked: + begin_cycles_node.hide = True + cycles_output_node.hide = True begin_cycles_node = cycles_output_node.inputs['Volume'].links[0].from_node node_type = begin_cycles_node.bl_idname bxdf_name, convert_func = _BSDF_MAP_.get(node_type, (None, None)) diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index c5474207..5ce539c1 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -33,6 +33,7 @@ def convert_cycles_node(nt, node, location=None): node_type = node.bl_idname + node.hide = True if node.name in converted_nodes: return nt.nodes[converted_nodes[node.name]] @@ -73,6 +74,8 @@ def convert_cycles_node(nt, node, location=None): node2 = node.inputs[ 1 + i].links[0].from_node if node.inputs[1 + i].is_linked else None + node1.hide = True + node2.hide = True if node.bl_idname == 'ShaderNodeAddShader': node_name = __BL_NODES_MAP__.get('LamaAdd') add = nt.nodes.new(node_name) @@ -157,6 +160,7 @@ def convert_linked_node(nt, socket, rman_node, param_name): (socket.node.location - socket.links[0].from_node.location) node = convert_cycles_node(nt, socket.links[0].from_node, location) if node: + node.hide = True out_socket = None # find the appropriate socket to hook up. diff --git a/rman_operators/rman_operators_nodetree.py b/rman_operators/rman_operators_nodetree.py index 716567c1..e25adf7c 100644 --- a/rman_operators/rman_operators_nodetree.py +++ b/rman_operators/rman_operators_nodetree.py @@ -96,6 +96,7 @@ def execute(self, context): light.type = 'POINT' light.renderman.use_renderman_node = True + shadergraph_utils.hide_cycles_nodes(light) output = nt.nodes.new('RendermanOutputNode') node_name = rman_bl_nodes.__BL_NODES_MAP__[light_shader] @@ -213,6 +214,7 @@ def execute(self, context): nt = idblock.node_tree if idtype == 'material': + shadergraph_utils.hide_cycles_nodes(idblock) output = nt.nodes.new('RendermanOutputNode') if idblock.grease_pencil: shadergraph_utils.convert_grease_pencil_mat(idblock, nt, output) @@ -263,6 +265,7 @@ def execute(self, context): light.type = 'POINT' light.renderman.use_renderman_node = True + shadergraph_utils.hide_cycles_nodes(light) output = nt.nodes.new('RendermanOutputNode') default = nt.nodes.new('%sLightNode' % @@ -283,6 +286,7 @@ def execute(self, context): elif idtype == 'world': # world + shadergraph_utils.hide_cycles_nodes(idblock) idblock.renderman.use_renderman_node = True if shadergraph_utils.find_node(idblock, 'RendermanIntegratorsOutputNode'): return {'FINISHED'} @@ -362,6 +366,7 @@ def execute(self, context): world.renderman.use_renderman_node = True if shadergraph_utils.find_node(world, 'RendermanIntegratorsOutputNode'): return {'FINISHED'} + shadergraph_utils.hide_cycles_nodes(world) output = nt.nodes.new('RendermanIntegratorsOutputNode') node_name = rman_bl_nodes.__BL_NODES_MAP__.get('PxrPathTracer') default = nt.nodes.new(node_name) @@ -395,7 +400,7 @@ def execute(self, context): world.renderman.use_renderman_node = True if shadergraph_utils.find_node(world, 'RendermanDisplayfiltersOutputNode'): return {'FINISHED'} - + shadergraph_utils.hide_cycles_nodes(world) df_output = nt.nodes.new('RendermanDisplayfiltersOutputNode') df_output.location = df_output.location df_output.location[0] -= 300 @@ -430,7 +435,7 @@ def execute(self, context): world = context.scene.world world.use_nodes = True nt = world.node_tree - + shadergraph_utils.hide_cycles_nodes(world) output = nt.nodes.new('RendermanIntegratorsOutputNode') node_name = rman_bl_nodes.__BL_NODES_MAP__.get('PxrPathTracer') default = nt.nodes.new(node_name) @@ -476,7 +481,7 @@ def execute(self, context): world.renderman.use_renderman_node = True if shadergraph_utils.find_node(world, 'RendermanSamplefiltersOutputNode'): return {'FINISHED'} - + shadergraph_utils.hide_cycles_nodes(world) sf_output = nt.nodes.new('RendermanSamplefiltersOutputNode') sf_output.location = sf_output.location sf_output.location[0] -= 300 @@ -511,7 +516,7 @@ def execute(self, context): ob.active_material = mat mat.use_nodes = True nt = mat.node_tree - + shadergraph_utils.hide_cycles_nodes(mat) output = nt.nodes.new('RendermanOutputNode') bxdf_node_name = rman_bl_nodes.__BL_NODES_MAP__[bxdf_name] default = nt.nodes.new(bxdf_node_name) @@ -572,6 +577,7 @@ def execute(self, context): ob.renderman.rman_material_override = mat mat.use_nodes = True nt = mat.node_tree + shadergraph_utils.hide_cycles_nodes(mat) output = nt.nodes.new('RendermanOutputNode') output.select = False diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index 8a025102..efc22ec7 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -202,6 +202,7 @@ def execute(self, context): light.renderman.renderman_lock_light_type = True light.use_nodes = True light.renderman.use_renderman_node = True + shadergraph_utils.hide_cycles_nodes(light) nt = light.node_tree output = nt.nodes.new('RendermanOutputNode') @@ -249,6 +250,7 @@ def execute(self, context): light_filter.renderman.renderman_lock_light_type = True light_filter.use_nodes = True light_filter.renderman.use_renderman_node = True + shadergraph_utils.hide_cycles_nodes(light_filter) nt = light_filter.node_tree output = nt.nodes.new('RendermanOutputNode') @@ -327,9 +329,9 @@ class PRMAN_OT_RM_Create_MeshLight(bpy.types.Operator): def create_mesh_light_material(self, context): mat = bpy.data.materials.new("PxrMeshLight") - mat.use_nodes = True nt = mat.node_tree + shadergraph_utils.hide_cycles_nodes(mat) output = nt.nodes.new('RendermanOutputNode') geoLight = nt.nodes.new('PxrMeshLightLightNode') diff --git a/rman_presets/core.py b/rman_presets/core.py index 5236bcd8..f23edfe1 100644 --- a/rman_presets/core.py +++ b/rman_presets/core.py @@ -1058,6 +1058,7 @@ def createNodes(Asset): mat = bpy.data.materials.new(Asset.label()) mat.use_nodes = True nt = mat.node_tree + shadergraph_utils.hide_cycles_nodes(mat) # create output node output_node = nt.nodes.new('RendermanOutputNode') From 785f54604fa1592152c48c4a10eca8bb1ef1c6d1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 17 May 2022 14:35:05 -0700 Subject: [PATCH 122/278] We can now select a face map to as a list of faces we want to tag as holes, when rendering subdivs. --- rman_config/config/rman_properties_mesh.json | 17 +++++++++++++ rman_translators/rman_mesh_translator.py | 26 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/rman_config/config/rman_properties_mesh.json b/rman_config/config/rman_properties_mesh.json index 736a9428..2ce865f5 100644 --- a/rman_config/config/rman_properties_mesh.json +++ b/rman_config/config/rman_properties_mesh.json @@ -86,6 +86,23 @@ "conditionalVisValue": "none" } }, + { + "panel": "MESH_PT_renderman_mesh_attrs", + "name": "rman_holesFaceMap", + "label": "Holes Face Map", + "type": "string", + "default": "", + "page": "Subdivision Mesh", + "editable": true, + "widget": "propSearch", + "options": "prop_parent:context.object|prop_name:face_maps", + "help": "You can select a face map to act as holes for your subdiv.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "rman_subdiv_scheme", + "conditionalVisValue": "none" + } + }, { "panel": "MESH_PT_renderman_prim_vars", "name": "export_default_uv", diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 870e4659..286d9182 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -8,6 +8,7 @@ import bpy import math +import bmesh import numpy as np def _get_mats_faces_(nverts, material_ids): @@ -242,6 +243,7 @@ def __init__(self, rman_scene): def _get_subd_tags_(self, ob, mesh, primvar): creases = [] + rm = mesh.renderman # only do creases 1 edge at a time for now, # detecting chains might be tricky.. @@ -266,6 +268,30 @@ def _get_subd_tags_(self, ob, mesh, primvar): intargs.extend([c[0], c[1]]) floatargs.append(c[2]) + holes_facemap = getattr(rm, 'rman_holesFaceMap', '') + if holes_facemap != '' and holes_facemap in ob.face_maps: + # use this facemap for face edit holes + holes_idx = ob.face_maps[holes_facemap].index + bm = bmesh.new() + bm.from_mesh(mesh) + fm = bm.faces.layers.face_map.verify() + + holes = [] + for face in bm.faces: + face_idx = face.index + map_idx = face[fm] + if map_idx == holes_idx: + holes.append(face_idx) + if holes: + tags.append('faceedit') + num_holes = len(holes) + for h in holes: + intargs.extend([1, h]) + nargs.extend([num_holes*2, 0, num_holes]) + stringargs.extend(['hole'] * num_holes) + + bm.free() + primvar.SetStringArray(self.rman_scene.rman.Tokens.Rix.k_Ri_subdivtags, tags, len(tags)) primvar.SetIntegerArray(self.rman_scene.rman.Tokens.Rix.k_Ri_subdivtagnargs, nargs, len(nargs)) primvar.SetIntegerArray(self.rman_scene.rman.Tokens.Rix.k_Ri_subdivtagintargs, intargs, len(intargs)) From 89e072a10cc251d8b62552f7d432b70ac9d5d80d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 18 May 2022 13:01:00 -0700 Subject: [PATCH 123/278] Use numpy to get edge creases for meshes. --- rman_translators/rman_mesh_translator.py | 40 ++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 286d9182..59f4b22e 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -242,18 +242,8 @@ def __init__(self, rman_scene): self.bl_type = 'MESH' def _get_subd_tags_(self, ob, mesh, primvar): - creases = [] rm = mesh.renderman - # only do creases 1 edge at a time for now, - # detecting chains might be tricky.. - for e in mesh.edges: - if e.crease > 0.0: - creases.append((e.vertices[0], e.vertices[1], - e.crease * e.crease * 10)) - # squared, to match blender appareance better - #: range 0 - 10 (infinitely sharp) - tags = ['interpolateboundary', 'facevaryinginterpolateboundary'] nargs = [1, 0, 0, 1, 0, 0] intargs = [ int(ob.data.renderman.rman_subdivInterp), @@ -261,12 +251,30 @@ def _get_subd_tags_(self, ob, mesh, primvar): floatargs = [] stringargs = [] - if len(creases) > 0: - for c in creases: - tags.append('crease') - nargs.extend([2, 1, 0]) - intargs.extend([c[0], c[1]]) - floatargs.append(c[2]) + # get creases + edges_len = len(mesh.edges) + creases = np.zeros(edges_len, dtype=np.float32) + mesh.edges.foreach_get('crease', creases) + if (creases > 0.0).any(): + # we have edges where their crease is > 0.0 + # grab only those edges + crease_edges = np.zeros(edges_len*2, dtype=np.int) + mesh.edges.foreach_get('vertices', crease_edges) + crease_edges = np.reshape(crease_edges, (edges_len, 2)) + crease_edges = crease_edges[creases > 0.0] + + # squared, to match blender appareance better + #: range 0 - 10 (infinitely sharp) + creases = creases * creases * 10.0 + + creases = creases[creases > 0.0] + edges_subset_len = len(creases) + + tags.extend(['crease'] * edges_subset_len) + nargs.extend([2, 1, 0] * edges_subset_len) + intargs.extend(crease_edges.flatten().tolist()) + floatargs.extend(creases.tolist()) + holes_facemap = getattr(rm, 'rman_holesFaceMap', '') if holes_facemap != '' and holes_facemap in ob.face_maps: From 522643ad56cc5a805f897a345d899dc0263591f2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 18 May 2022 15:47:16 -0700 Subject: [PATCH 124/278] Shave another second off of the export time for the Louise scene, by using numpy to create the indices array for the curves. --- rman_translators/rman_curve_translator.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/rman_translators/rman_curve_translator.py b/rman_translators/rman_curve_translator.py index 05e2ff5d..f0e257e7 100644 --- a/rman_translators/rman_curve_translator.py +++ b/rman_translators/rman_curve_translator.py @@ -14,9 +14,8 @@ def get_bspline_curve(curve): nvertices = [] name = '' num_curves = len(curve.splines) - index = [] - for i, spline in enumerate(curve.splines): + for spline in curve.splines: npoints = len(spline.points) pts = np.zeros(npoints*4, dtype=np.float32) @@ -29,9 +28,10 @@ def get_bspline_curve(curve): P.extend(pts[0:, 0:3].tolist()) widths.append(width.tolist()) - index.append(i) - nvertices.append(len(spline.points)) + nvertices.append(npoints) name = spline.id_data.name + + index = np.arange(num_curves).tolist() return (P, num_curves, nvertices, widths, index, name) @@ -41,9 +41,8 @@ def get_curve(curve): nvertices = [] name = '' num_curves = len(curve.splines) - index = [] - for i, spline in enumerate(curve.splines): + for spline in curve.splines: npoints = len(spline.points) pts = np.zeros(npoints*4, dtype=np.float32) @@ -55,11 +54,11 @@ def get_curve(curve): width = np.where(width >= 1.0, width*0.01, 0.01) P.extend(pts[0:, 0:3].tolist()) - widths.append(width) - index.append(i) - nvertices.append(len(spline.points)) + nvertices.append(npoints) name = spline.id_data.name + + index = np.arange(num_curves).tolist() return (P, num_curves, nvertices, widths, index, name) From d21bfc7bcc1f18a185fac1679f8e3feff8f38d03 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 May 2022 07:21:31 -0700 Subject: [PATCH 125/278] Move get_mesh and get_mesh_points functions into a mesh_utils.py file. --- rfb_utils/mesh_utils.py | 61 ++++++++++++++++++++++ rfb_utils/object_utils.py | 42 +-------------- rman_translators/rman_mesh_translator.py | 5 +- rman_translators/rman_points_translator.py | 10 ++-- 4 files changed, 68 insertions(+), 50 deletions(-) create mode 100644 rfb_utils/mesh_utils.py diff --git a/rfb_utils/mesh_utils.py b/rfb_utils/mesh_utils.py new file mode 100644 index 00000000..b1ba1d03 --- /dev/null +++ b/rfb_utils/mesh_utils.py @@ -0,0 +1,61 @@ +import numpy as np + +def get_mesh_points_(mesh): + ''' + Get just the points for the input mesh. + + Arguments: + mesh (bpy.types.Mesh) - Blender mesh + + Returns: + (list) - the points on the mesh + ''' + + nvertices = len(mesh.vertices) + P = np.zeros(nvertices*3, dtype=np.float32) + mesh.vertices.foreach_get('co', P) + P = np.reshape(P, (nvertices, 3)) + return P.tolist() + +def get_mesh(mesh, get_normals=False): + ''' + Get the basic primvars needed to render a mesh. + + Arguments: + mesh (bpy.types.Mesh) - Blender mesh + get_normals (bool) - Whether or not normals are needed + + Returns: + (list) - this includes nverts (the number of vertices for each face), + vertices list, points, and normals + ''' + + P = get_mesh_points_(mesh) + N = [] + + npolygons = len(mesh.polygons) + fastnvertices = np.zeros(npolygons, dtype=np.int) + mesh.polygons.foreach_get('loop_total', fastnvertices) + nverts = fastnvertices.tolist() + + loops = len(mesh.loops) + fastvertices = np.zeros(loops, dtype=np.int) + mesh.loops.foreach_get('vertex_index', fastvertices) + verts = fastvertices.tolist() + + if get_normals: + fastsmooth = np.zeros(npolygons, dtype=np.int) + mesh.polygons.foreach_get('use_smooth', fastsmooth) + if mesh.use_auto_smooth or True in fastsmooth: + mesh.calc_normals_split() + fastnormals = np.zeros(loops*3, dtype=np.float32) + mesh.loops.foreach_get('normal', fastnormals) + fastnormals = np.reshape(fastnormals, (loops, 3)) + N = fastnormals.tolist() + else: + fastnormals = np.zeros(npolygons*3, dtype=np.float32) + mesh.polygons.foreach_get('normal', fastnormals) + fastnormals = np.reshape(fastnormals, (npolygons, 3)) + N = fastnormals.tolist() + + return (nverts, verts, P, N) \ No newline at end of file diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 255d2143..9abd05cd 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -1,5 +1,4 @@ import bpy -import numpy as np from .prefs_utils import get_pref from . import string_utils @@ -267,43 +266,4 @@ def _get_used_materials_(ob): break return [mesh.materials[i] for i in mat_ids] else: - return [ob.active_material] - -def _get_mesh_points_(mesh): - nvertices = len(mesh.vertices) - P = np.zeros(nvertices*3, dtype=np.float32) - mesh.vertices.foreach_get('co', P) - P = np.reshape(P, (nvertices, 3)) - return P.tolist() - -def _get_mesh_(mesh, get_normals=False): - - P = _get_mesh_points_(mesh) - N = [] - - npolygons = len(mesh.polygons) - fastnvertices = np.zeros(npolygons, dtype=np.int) - mesh.polygons.foreach_get('loop_total', fastnvertices) - nverts = fastnvertices.tolist() - - loops = len(mesh.loops) - fastvertices = np.zeros(loops, dtype=np.int) - mesh.loops.foreach_get('vertex_index', fastvertices) - verts = fastvertices.tolist() - - if get_normals: - fastsmooth = np.zeros(npolygons, dtype=np.int) - mesh.polygons.foreach_get('use_smooth', fastsmooth) - if mesh.use_auto_smooth or True in fastsmooth: - mesh.calc_normals_split() - fastnormals = np.zeros(loops*3, dtype=np.float32) - mesh.loops.foreach_get('normal', fastnormals) - fastnormals = np.reshape(fastnormals, (loops, 3)) - N = fastnormals.tolist() - else: - fastnormals = np.zeros(npolygons*3, dtype=np.float32) - mesh.polygons.foreach_get('normal', fastnormals) - fastnormals = np.reshape(fastnormals, (npolygons, 3)) - N = fastnormals.tolist() - - return (nverts, verts, P, N) \ No newline at end of file + return [ob.active_material] \ No newline at end of file diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 59f4b22e..019bb2db 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -1,6 +1,7 @@ from .rman_translator import RmanTranslator from ..rman_sg_nodes.rman_sg_mesh import RmanSgMesh from ..rfb_utils import object_utils +from ..rfb_utils import mesh_utils from ..rfb_utils import string_utils from ..rfb_utils import property_utils from ..rfb_utils import scenegraph_utils @@ -324,7 +325,7 @@ def export_deform_sample(self, rman_sg_mesh, ob, time_sample, sg_node=None): if not sg_node: sg_node = rman_sg_mesh.sg_node primvar = sg_node.GetPrimVars() - P = object_utils._get_mesh_points_(mesh) + P = mesh_utils.get_mesh_points_(mesh) npoints = len(P) if rman_sg_mesh.npoints != npoints: @@ -365,7 +366,7 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): rman_sg_mesh.is_subdiv = object_utils.is_subdmesh(ob) use_smooth_normals = getattr(ob.data.renderman, 'rman_smoothnormals', False) get_normals = (rman_sg_mesh.is_subdiv == 0 and not use_smooth_normals) - (nverts, verts, P, N) = object_utils._get_mesh_(mesh, get_normals=get_normals) + (nverts, verts, P, N) = mesh_utils.get_mesh(mesh, get_normals=get_normals) # if this is empty continue: if nverts == []: diff --git a/rman_translators/rman_points_translator.py b/rman_translators/rman_points_translator.py index bdf061da..92946ec6 100644 --- a/rman_translators/rman_points_translator.py +++ b/rman_translators/rman_points_translator.py @@ -1,10 +1,6 @@ from .rman_translator import RmanTranslator from ..rman_sg_nodes.rman_sg_points import RmanSgPoints -from ..rfb_utils import object_utils -from ..rfb_utils import string_utils - -import bpy -import math +from ..rfb_utils import mesh_utils class RmanPointsTranslator(RmanTranslator): @@ -23,7 +19,7 @@ def export_deform_sample(self, rman_sg_points, ob, time_sample): mesh = None mesh = ob.to_mesh() - P = object_utils._get_mesh_points_(mesh) + P = mesh_utils.get_mesh_points_(mesh) primvar = rman_sg_points.sg_node.GetPrimVars() npoints = len(P) @@ -47,7 +43,7 @@ def update(self, ob, rman_sg_points, input_mesh=None): if not mesh: mesh = ob.to_mesh() - P = object_utils._get_mesh_points_(mesh) + P = mesh_utils.get_mesh_points_(mesh) # if this is empty continue: if not P or len(P) < 1: From 7010b2eefc4e9d58e41fc1648afd2a76fa239dc6 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 May 2022 08:24:24 -0700 Subject: [PATCH 126/278] Add a conversion function for cycle's wireframe shader. --- rman_cycles_convert/cycles_convert.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index 5ce539c1..e899cd34 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -371,6 +371,21 @@ def convert_math_node(nt, cycles_node, rman_node): return +def convert_wireframe_node(nt, cycles_node, rman_node): + + tmp = [rman_node.wireColor[0], rman_node.wireColor[1], rman_node.wireColor[2]] + rman_node.wireColor = [rman_node.backColor[0], rman_node.backColor[1], rman_node.backColor[2]] + rman_node.backColor = [tmp[0], tmp[1], tmp[2]] + + input = cycles_node.inputs['Size'] + if input.is_linked: + convert_linked_node(nt, input, rman_node, input.name) + else: + val = input.default_value + rman_node.wireWidth = val * 100.0 + + return + # this needs a special case to init the stuff @@ -778,5 +793,6 @@ def convert_volume_principled(nt, node, rman_node): 'ShaderNodeMath': ('', convert_math_node), 'ShaderNodeRGB': ('PxrHSL', convert_rgb_node), 'ShaderNodeValue': ('PxrToFloat', convert_node_value), - 'ShaderNodeAttribute': ('PxrPrimvar', convert_attribute_node) + 'ShaderNodeAttribute': ('PxrPrimvar', convert_attribute_node), + 'ShaderNodeWireframe': ('PxrWireframe', convert_wireframe_node) } From 423b310db3da5a40a7e8385b844fb973d46a07b1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 May 2022 10:32:00 -0700 Subject: [PATCH 127/278] Add a basic conversion path for cycles' geometry shading node --- rman_cycles_convert/cycles_convert.py | 87 ++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index e899cd34..0f44e0c0 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -154,13 +154,96 @@ def set_color_space(nt, socket, rman_node, node, param_name, in_socket): if node.bl_label in ['PxrTexture'] and shadergraph_utils.is_socket_float_type(in_socket): setattr(node, 'filename_colorspace', 'data') +def convert_new_geometry_node(nt, socket, cycles_node, rman_node, param_name): + socket_nm = socket.links[0].from_socket.name + in_socket = rman_node.inputs[param_name] + if socket_nm == 'Backfacing': + node_name = __BL_NODES_MAP__.get('PxrShadedSide', None) + convert_node = nt.nodes.new(node_name) + convert_node.invert = 1 + nt.links.new(convert_node.outputs['resultF'], in_socket) + elif socket_nm == 'Incoming': + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'Vn' + convert_node.type = 'vector' + nt.links.new(convert_node.outputs['resultP'], in_socket) + elif socket_nm == 'Normal': + # The Blender docs says this also includes bump mapping + # Have to think about how to wire the result of any PxrBumps in the network + # to here + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'Nn' + convert_node.type = 'normal' + nt.links.new(convert_node.outputs['resultP'], in_socket) + elif socket_nm == 'Parametric': + # From the Blender docs: + # + # "Parametric coordinates of the shading point on the surface. + # To area lights it outputs its UV coordinates in planar mapping and + # in spherical coordinates to point lights." + # + # + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'uvw' + convert_node.type = 'vector' + nt.links.new(convert_node.outputs['resultP'], in_socket) + elif socket_nm == 'Pointiness': + # From the Blender docs: + # + # "An approximation of the curvature of the mesh per vertex. Lighter + # values indicate convex angles, darker values indicate concave angles. + # It allows you to do effects like dirt maps and wear-off effects." + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'curvature' + convert_node.type = 'float' + nt.links.new(convert_node.outputs['resultF'], in_socket) + elif socket_nm == 'Position': + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'P' + convert_node.type = 'point' + nt.links.new(convert_node.outputs['resultP'], in_socket) + elif socket_nm == 'Random Per Island': + # From the Blender docs: + # + # "A random value for each connected component (island) of the mesh. + # It is useful to add variations to meshes composed of separated units like + # tree leaves, wood planks, or curves of multiple splines." + # + # Not exactly sure how to convert this. For now, we'll just use PxrVary. + # PxrVary doesn't have a float output, so we'll just use resultR + node_name = __BL_NODES_MAP__.get('PxrVary', None) + convert_node = nt.nodes.new(node_name) + nt.links.new(convert_node.outputs['resultR'], in_socket) + elif socket_nm == 'Tangent': + # Tangent at the surface. + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'Tn' + convert_node.type = 'vector' + nt.links.new(convert_node.outputs['resultP'], in_socket) + elif socket_nm == 'True Normal': + # Geometry or flat normal of the surface. + node_name = __BL_NODES_MAP__.get('PxrPrimvar', None) + convert_node = nt.nodes.new(node_name) + convert_node.variable = 'Ngn' + convert_node.type = 'normal' + nt.links.new(convert_node.outputs['resultP'], in_socket) def convert_linked_node(nt, socket, rman_node, param_name): location = rman_node.location - \ (socket.node.location - socket.links[0].from_node.location) - node = convert_cycles_node(nt, socket.links[0].from_node, location) + from_node = socket.links[0].from_node + if from_node.bl_idname == 'ShaderNodeNewGeometry': + # this node needs special handling + return convert_new_geometry_node(nt, socket, from_node, rman_node, param_name) + + node = convert_cycles_node(nt, from_node, location) if node: - node.hide = True out_socket = None # find the appropriate socket to hook up. From f45e2ee3196595417792fc2ac1ccfbf54213cf68 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 May 2022 11:37:13 -0700 Subject: [PATCH 128/278] Add a subscription to /rman/renderer@isRendering stat. Not currently used, but we should use it once XPU has added support for it. --- rman_render.py | 1 + rman_stats/__init__.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rman_render.py b/rman_render.py index 33ea9621..01ca2bde 100644 --- a/rman_render.py +++ b/rman_render.py @@ -566,6 +566,7 @@ def start_stats_thread(self): # FIXME: for now, add a 1 second delay before starting the stats thread # for some reason, XPU doesn't seem to reset the progress between renders time.sleep(1.0) + self.stats_mgr.reset() __RMAN_STATS_THREAD__.start() def reset(self): diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index 3f9b5804..3c0ef257 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -19,6 +19,7 @@ __LIVE_METRICS__ = [ ["/system.processMemory", "Memory"], + ["/rman/renderer@isRendering", None], ["/rman/renderer@progress", None], ['/rman@iterationComplete', None], ["/rman.timeToFirstRaytrace", "First Ray"], @@ -110,6 +111,7 @@ def __init__(self, rman_render): self._prevTotalRays = 0 self._progress = 0 self._prevTotalRaysValid = True + self._isRendering = False for name,label in __LIVE_METRICS__: if name: @@ -157,7 +159,8 @@ def reset(self): self._progress = 0 self._prevTotalRaysValid = True self.export_stat_label = '' - self.export_stat_progress = 0.0 + self.export_stat_progress = 0.0 + self._isRendering = True def create_stats_manager(self): if self.mgr: @@ -355,6 +358,9 @@ def update_payloads(self): self.render_live_stats["Total Rays"] = currentTotalRays self._prevTotalRaysValid = True self._prevTotalRays = currentTotalRays + elif name == "/rman/renderer@isRendering": + is_rendering = dat['payload'] + self._isRendering = is_rendering elif name == "/rman@iterationComplete": itr = dat['payload'][0] self._iterations = itr From 182136221ff8d1559770b4288057f7a88a77677c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 May 2022 11:56:57 -0700 Subject: [PATCH 129/278] Fix a couple of typos: * bxdf_input -> bxdf_in * displacement_input -> displace_in --- rman_presets/core.py | 4 ++-- rman_ui/rman_ui_object_panels.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rman_presets/core.py b/rman_presets/core.py index f23edfe1..66620c92 100644 --- a/rman_presets/core.py +++ b/rman_presets/core.py @@ -590,14 +590,14 @@ def export_material_preset(mat, nodes_to_convert, renderman_output_node, Asset): dstPlug = "%s.%s" % (nodeName, 'rman__surface') Asset.addConnection(srcPlug, dstPlug) - if renderman_output_node.inputs['displacement_input'].is_linked: + if renderman_output_node.inputs['displace_in'].is_linked: infodict = {} infodict['name'] = 'rman__displacement' infodict['type'] = 'reference float3' infodict['value'] = None Asset.addParam(nodeName, nodeType, 'rman__displacement', infodict) - from_node = renderman_output_node.inputs['displacement_input'].links[0].from_node + from_node = renderman_output_node.inputs['displace_in'].links[0].from_node srcPlug = "%s.%s" % (fix_blender_name(from_node.name), 'outColor') dstPlug = "%s.%s" % (nodeName, 'rman__displacement') Asset.addConnection(srcPlug, dstPlug) diff --git a/rman_ui/rman_ui_object_panels.py b/rman_ui/rman_ui_object_panels.py index dd2e32e6..35c1e0ab 100644 --- a/rman_ui/rman_ui_object_panels.py +++ b/rman_ui/rman_ui_object_panels.py @@ -233,7 +233,7 @@ def draw(self, context): col = split.column() layout.separator() - input_name = 'bxdf_input' + input_name = 'bxdf_in' if not rman_output_node.inputs[input_name].is_linked: panel_node_draw(layout, context, mat, 'RendermanOutputNode', 'Bxdf') @@ -340,7 +340,7 @@ def draw(self, context): col = split.column() shader_type = 'Displacement' - input_name = 'displacement_input' + input_name = 'displace_in' if not rman_output_node.inputs[input_name].is_linked: draw_nodes_properties_ui( layout, context, nt, input_name=input_name) From 9eef32d2a2c09891599c830bb0b8ef0a10fc9984 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 20 May 2022 09:34:43 -0700 Subject: [PATCH 130/278] Add "user:blender_is_instance" and "user:blender_instance_uv" user attributes to each instance. The first attribute indicates whether this instance is a blender instance, and the second contains the UV. --- rman_translators/rman_translator.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 621c756f..82533be6 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -88,7 +88,8 @@ def export_object_id(self, ob, rman_sg_node, ob_inst): if not rman_sg_node.sg_node: return name = ob.name_full - if ob_inst.is_instance: + is_instance = ob_inst.is_instance + if is_instance: name = ob_inst.parent.name attrs = rman_sg_node.sg_node.GetAttributes() rman_type = object_utils._detect_primitive_(ob) @@ -109,7 +110,13 @@ def export_object_id(self, ob, rman_sg_node, ob_inst): ]: id = int(hashlib.sha1(rman_sg_node.db_name.encode()).hexdigest(), 16) % 10**8 procprimid = float(id) - attrs.SetFloat('user:procprimid', procprimid) + attrs.SetFloat('user:procprimid', procprimid) + + if is_instance: + attrs.SetFloat('user:blender_is_instance', 1) + attrs.SetFloatArray('user:blender_instance_uv', ob_inst.uv, 2) + else: + attrs.SetFloat('user:blender_is_instance', 0) rman_sg_node.sg_node.SetAttributes(attrs) From 21982f39149f3d500b1963e9f4035ed0c357506f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 20 May 2022 15:05:46 -0700 Subject: [PATCH 131/278] Fix an issue where assigning a new material to the instanced object was removing the instancer object. --- rman_scene.py | 6 +++--- rman_scene_sync.py | 32 ++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index f1a6d236..fc5a5e96 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -594,7 +594,7 @@ def is_instance_selected(self, instance): return True - def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): + def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys, foo=True): rman_group_translator = self.rman_translators['GROUP'] group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) @@ -639,11 +639,11 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) if rman_parent_node: if group_db_name in rman_parent_node.instances: - del rman_parent_node[group_db_name] + del rman_parent_node.instances[group_db_name] rman_parent_node.instances[group_db_name] = rman_sg_group else: if group_db_name in rman_sg_node.instances: - del rman_sg_node[group_db_name] + del rman_sg_node.instances[group_db_name] rman_sg_node.instances[group_db_name] = rman_sg_group if rman_type == "META": diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a770c1db..a1c7bf3b 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -798,15 +798,16 @@ def check_instances(self): if rman_sg_node.is_frame_sensitive and self.frame_number_changed: rman_update.is_updated_geometry = True self.rman_updates[ob_key] = rman_update - else: - if ob_key not in self.rman_updates: - if not instance_parent: - continue - if instance_parent.original not in self.rman_updates: - continue - rman_update = self.rman_updates[instance_parent.original] - else: - rman_update = self.rman_updates[ob_key] + elif ob_key in self.rman_updates: + rman_update = self.rman_updates[ob_key] + else: + # check if the instance_parent was the thing that + # changed + if not instance_parent: + continue + if instance_parent.original not in self.rman_updates: + continue + rman_update = self.rman_updates[instance_parent.original] if rman_sg_node and not is_new_object and not instance.is_instance: if rman_update.is_updated_geometry and proto_key not in already_udpated: @@ -828,10 +829,13 @@ def check_instances(self): parent_proto_key = object_utils.prototype_key(instance_parent) rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) if rman_parent_node and rman_parent_node not in clear_instances: - rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) - rman_parent_node.clear_instances() - clear_instances.append(rman_parent_node) - elif rman_sg_node not in clear_instances: + pass + ## Not sure if we need to do this? + + #rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) + #rman_parent_node.clear_instances() + #clear_instances.append(rman_parent_node) + if rman_sg_node not in clear_instances: rfb_log().debug("\tClearing instances: %s" % proto_key) rman_sg_node.clear_instances() clear_instances.append(rman_sg_node) @@ -841,7 +845,7 @@ def check_instances(self): # add an instance of it continue - self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys, foo=False) if rman_type == 'LIGHT': # We are dealing with a light. Check if it's a solo light, or muted From 5fcf533f50ce7150e4b4f840d261052a1ed7cd32 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 20 May 2022 15:07:15 -0700 Subject: [PATCH 132/278] Need to do same fix for the batch render case. --- rman_scene_sync.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a1c7bf3b..ec3ee7fe 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -597,9 +597,12 @@ def check_instances_batch(self): parent_proto_key = object_utils.prototype_key(instance_parent) rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) if rman_parent_node and rman_parent_node not in clear_instances: - rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) - rman_parent_node.clear_instances() - clear_instances.append(rman_parent_node) + pass + ## Not sure if we need to do this? + + #rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) + #rman_parent_node.clear_instances() + #clear_instances.append(rman_parent_node) elif rman_sg_node not in clear_instances: rfb_log().debug("\tClearing instances: %s" % proto_key) rman_sg_node.clear_instances() From db9235c1e8815734d4b98f2dac833fdc03880cb0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 20 May 2022 15:45:44 -0700 Subject: [PATCH 133/278] * Rename export_object_id to export_instance_attributes in rman_translator. * To help speed up editing of instances, we cache the object attributes that should be shared amongst all of the instances. * Fix a couple of comment typos --- rman_scene.py | 13 +++++++++++-- rman_scene_sync.py | 2 ++ rman_sg_nodes/rman_sg_node.py | 9 +++++---- rman_translators/rman_translator.py | 9 +++++---- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index fc5a5e96..ce8ac852 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -606,10 +606,19 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa # Object attrs translator = self.rman_translators.get(rman_type, None) if translator: - translator.export_object_attributes(ob_eval, rman_sg_group) + if rman_sg_node.shared_attrs.GetNumParams() == 0: + # export the attributes for this object + translator.export_object_attributes(ob_eval, rman_sg_group) + rman_sg_node.shared_attrs.Inherit(rman_sg_group.sg_node.GetAttributes()) + else: + # the attributes of this object have already been exported + # just call SetAttributes + rman_sg_group.sg_node.SetAttributes(rman_sg_node.shared_attrs) + if is_empty_instancer: translator.export_object_attributes(instance_parent, rman_sg_group, remove=False) - translator.export_object_id(ob_eval, rman_sg_group, ob_inst) + + translator.export_instance_attributes(ob_eval, rman_sg_group, ob_inst) # Add any particles necessary if rman_sg_node.rman_sg_particle_group_node: diff --git a/rman_scene_sync.py b/rman_scene_sync.py index ec3ee7fe..64b04c9d 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -582,6 +582,7 @@ def check_instances_batch(self): translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) translator.update(ob_eval, rman_sg_node) + rman_sg_node.shared_attrs.Clear() self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) @@ -817,6 +818,7 @@ def check_instances(self): translator = self.rman_scene.rman_translators.get(rman_type, None) rfb_log().debug("\tUpdating Object: %s" % proto_key) translator.update(ob_eval, rman_sg_node) + rman_sg_node.shared_attrs.Clear() self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) diff --git a/rman_sg_nodes/rman_sg_node.py b/rman_sg_nodes/rman_sg_node.py index be2b0fd2..6f293c24 100644 --- a/rman_sg_nodes/rman_sg_node.py +++ b/rman_sg_nodes/rman_sg_node.py @@ -11,7 +11,7 @@ class RmanSgNode(object): db_name (str) - unique datablock name for this node instances (dict) - instances that uses this sg_node motion_steps (list) - the full list of motion time samples that are required for this Blender object - motion_steps (list) - the full list of deformation time samples that are required for this Blender object + deform_motion_steps (list) - the full list of deformation time samples that are required for this Blender object is_transforming (bool) - if this object is moving is_deforming (bool) - if this object is deforming rman_type (str) - the renderman type for this object @@ -19,6 +19,7 @@ class RmanSgNode(object): is_meshlight (bool) - if this object is a mesh light. is_hidden (bool) - whether this object is considered hidden is_frame_sensitive (bool) - indicates that the sg_node should be updated on frame changes + shared_attrs (RtParamList) - attributes that should be shared between all instances ''' def __init__(self, rman_scene, sg_node, db_name): @@ -50,12 +51,12 @@ def __init__(self, rman_scene, sg_node, db_name): # in texture paths self.is_frame_sensitive = False - # objects that this node creates as part of instancing - self.objects_instanced = set() - # psys self.bl_psys_settings = None + # attributes that should be shared with all instances + self.shared_attrs = rman_scene.rman.Types.ParamList() + def __del__(self): if self.rman_scene.rman_render.rman_running and self.rman_scene.rman_render.sg_scene: with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 82533be6..38a15f10 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -84,7 +84,10 @@ def export_object_primvars(self, ob, primvars): rm_scene = self.rman_scene.bl_scene.renderman property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) - def export_object_id(self, ob, rman_sg_node, ob_inst): + def export_instance_attributes(self, ob, rman_sg_node, ob_inst): + ''' + Export attributes that should vary between each instance + ''' if not rman_sg_node.sg_node: return name = ob.name_full @@ -196,8 +199,7 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): if conditionalVisOps: # check conditionalVisOps to see if this riattr applies # to this object - expr = conditionalVisOps.get('expr', None) - node = rm + expr = conditionalVisOps.get('expr', None) if expr and not eval(expr): continue @@ -206,7 +208,6 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): if 'inheritable' in meta: cond = meta['inherit_true_value'] if isinstance(cond, str): - node = rm if exec(cond): if remove: attrs.Remove(ri_name) From 99ff82c4c7791fc6969bc72b9e2c14125853d877 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 31 May 2022 11:51:09 -0700 Subject: [PATCH 134/278] Remove a debug parameter. --- rman_scene.py | 2 +- rman_scene_sync.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index ce8ac852..b54dcd40 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -594,7 +594,7 @@ def is_instance_selected(self, instance): return True - def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys, foo=True): + def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): rman_group_translator = self.rman_translators['GROUP'] group_db_name = object_utils.get_group_db_name(ob_inst) rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 64b04c9d..0ccb32eb 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -850,7 +850,7 @@ def check_instances(self): # add an instance of it continue - self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys, foo=False) + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) if rman_type == 'LIGHT': # We are dealing with a light. Check if it's a solo light, or muted From 2320d859adb2f1b098ec09afd9a190bb9a20296b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 31 May 2022 15:30:30 -0700 Subject: [PATCH 135/278] Fix problems when switching particles from emitter/hair to instances. We weren't properly removing the previous emitter/hair particles. --- rman_scene.py | 4 +- rman_scene_sync.py | 101 +++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index b54dcd40..46912d53 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -953,12 +953,12 @@ def get_rman_prototype(self, proto_key, ob=None, create=False): rman_sg_node = self.export_data_block(proto_key, ob) return rman_sg_node - def get_rman_particles(self, proto_key, psys, ob): + def get_rman_particles(self, proto_key, psys, ob, create=True): psys_translator = self.rman_translators['PARTICLES'] group_translator = self.rman_translators['GROUP'] ob_psys = self.rman_particles.get(proto_key, dict()) rman_sg_particles = ob_psys.get(psys.settings.original, None) - if not rman_sg_particles: + if not rman_sg_particles and create: psys_db_name = '%s' % psys.name rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) ob_psys[psys.settings.original] = rman_sg_particles diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 0ccb32eb..30c225b7 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -269,11 +269,12 @@ def check_particle_instancer(self, ob_update, psys): rman_update.is_updated_transform = ob_update.is_updated_transform self.rman_updates[col_obj.original] = rman_update - def update_particle_emitter(self, ob, psys): + def update_particle_emitter(self, ob, psys, delete=False): psys_translator = self.rman_scene.rman_translators['PARTICLES'] proto_key = object_utils.prototype_key(ob) - rman_sg_particles = self.rman_scene.get_rman_particles(proto_key, psys, ob) - psys_translator.update(ob, psys, rman_sg_particles) + rman_sg_particles = self.rman_scene.get_rman_particles(proto_key, psys, ob, create=delete) + if rman_sg_particles: + psys_translator.update(ob, psys, rman_sg_particles) def update_particle_emitters(self, ob): for psys in ob.particle_systems: @@ -385,23 +386,53 @@ def check_object_datablock(self, obj): self.light_filter_updated(obj) elif rman_type == 'CAMERA': self.camera_updated(obj) - else: - rfb_log().debug("\tObject: %s Updated" % obj.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - + else: if obj.id.original not in self.rman_updates: + rfb_log().debug("\tObject: %s Updated" % obj.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) rman_update = RmanUpdate() rman_update.is_updated_geometry = obj.is_updated_geometry rman_update.is_updated_shading = obj.is_updated_shading rman_update.is_updated_transform = obj.is_updated_transform self.rman_updates[obj.id.original] = rman_update + for psys in ob_eval.particle_systems: + if object_utils.is_particle_instancer(psys): + self.check_particle_instancer(obj, psys) + # Check if this object is the focus object the camera. If it is # we need to update the camera if obj.is_updated_transform: - self.check_focus_object(ob_eval) + self.check_focus_object(ob_eval) + + def check_particle_settings(self, obj): + rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) + + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + users = self.rman_scene.context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) + for o in users[obj.id.original]: + ob = o.evaluated_get(self.rman_scene.depsgraph) + psys = None + for ps in ob.particle_systems: + if ps.settings.original == obj.id.original: + psys = ps + break + if not psys: + continue + if object_utils.is_particle_instancer(psys): + # if this particle system was changed to an instancer + # make sure any old emitters/hair is removed + self.update_particle_emitter(ob, psys, delete=True) + else: + self.update_particle_emitter(ob, psys) + + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_shading = obj.is_updated_shading + rman_update.is_updated_transform = obj.is_updated_transform + self.rman_updates[o.original] = rman_update def check_shader_nodetree(self, obj): if obj.id.name in bpy.data.node_groups: @@ -459,29 +490,7 @@ def batch_update_scene(self, context, depsgraph): ob = obj.id if isinstance(obj.id, bpy.types.ParticleSettings): - rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) - for o in self.rman_scene.bl_scene.objects: - psys = None - ob = o.evaluated_get(depsgraph) - for ps in ob.particle_systems: - if ps.settings.original == obj.id.original: - psys = ps - break - if not psys: - continue - if object_utils.is_particle_instancer(psys): - #self.check_particle_instancer(obj, psys) - pass - else: - self.update_particle_emitter(ob, psys) - - if o.original not in self.rman_updates: - rman_update = RmanUpdate() - - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[o.original] = rman_update + self.check_particle_settings(obj) elif isinstance(obj.id, bpy.types.Light): self.check_light_datablock(obj) @@ -673,31 +682,7 @@ def update_scene(self, context, depsgraph): self.check_light_datablock(obj) elif isinstance(obj.id, bpy.types.ParticleSettings): - rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) - - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for o in self.rman_scene.bl_scene.objects: - psys = None - ob = o.evaluated_get(depsgraph) - for ps in ob.particle_systems: - if ps.settings.original == obj.id.original: - psys = ps - break - if not psys: - continue - if object_utils.is_particle_instancer(psys): - #self.check_particle_instancer(obj, psys) - pass - else: - self.update_particle_emitter(ob, psys) - - if o.original not in self.rman_updates: - rman_update = RmanUpdate() - - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform - self.rman_updates[o.original] = rman_update + self.check_particle_settings(obj) elif isinstance(obj.id, bpy.types.ShaderNodeTree): self.check_shader_nodetree(obj) From 5e7649dbced2610bb202042d8d3f3f1cc8f497a3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 1 Jun 2022 15:50:37 -0700 Subject: [PATCH 136/278] More fixes for instances during IPR: * if we detct that an object only transformed, we shouldn't need to clear the instances of the prototype. This should speed up editing in some cases. * consolidate some code; remove the check_instances_batch function * only add the instance to the parent rman_sg_node, if the instance is part of a particle system. --- rman_scene.py | 44 +++++--- rman_scene_sync.py | 243 ++++++++++++++++++--------------------------- 2 files changed, 123 insertions(+), 164 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 46912d53..331ae9e0 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -594,11 +594,36 @@ def is_instance_selected(self, instance): return True + def get_rman_sg_instance(self, ob_inst, rman_sg_node, instance_parent, psys, create=True): + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_parent_node = None + if psys and instance_parent: + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) + if rman_parent_node: + if group_db_name in rman_parent_node.instances: + return rman_parent_node.instances[group_db_name] + else: + if group_db_name in rman_sg_node.instances: + return rman_sg_node.instances[group_db_name] + + rman_sg_group = None + if create: + rman_group_translator = self.rman_translators['GROUP'] + rman_sg_group = rman_group_translator.export(None, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + + if rman_parent_node: + # this is an instance that comes from a particle system + # add this instance to the rman_sg_node that owns the particle system + rman_parent_node.instances[group_db_name] = rman_sg_group + else: + rman_sg_node.instances[group_db_name] = rman_sg_group + + return rman_sg_group + def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): rman_group_translator = self.rman_translators['GROUP'] - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_sg_group = rman_group_translator.export(ob_eval, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + rman_sg_group = self.get_rman_sg_instance(ob_inst, rman_sg_node, instance_parent, psys, create=True) is_empty_instancer = False if instance_parent and object_utils._detect_primitive_(instance_parent) == 'EMPTY_INSTANCER': is_empty_instancer = True @@ -644,23 +669,12 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa else: self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - if instance_parent: - rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) - if rman_parent_node: - if group_db_name in rman_parent_node.instances: - del rman_parent_node.instances[group_db_name] - rman_parent_node.instances[group_db_name] = rman_sg_group - else: - if group_db_name in rman_sg_node.instances: - del rman_sg_node.instances[group_db_name] - rman_sg_node.instances[group_db_name] = rman_sg_group - if rman_type == "META": # meta/blobbies are already in world space. Their instances don't need to # set a transform. return rman_sg_group - rman_group_translator.update_transform(ob_inst, rman_sg_group) + rman_group_translator.update_transform(ob_inst, rman_sg_group) return rman_sg_group diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 30c225b7..63f04dc5 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -12,11 +12,24 @@ import bpy class RmanUpdate: + ''' + The RmanUpdate class. A helper class to indicate what kind of update + we're dealing with + + Attributes: + is_updated_geometry (bool) - Whether the geometry has been updated. This doesn't necessarily + mean the actual geometry was updated. Attribute changes are also + considered geometry updates + is_updated_transform (bool) - Whether the geometry was transformed + is_updated_shading (bool) - If the shader on the object has changed + do_clear_instances (bool) - Whether we should clear/delete all instances of the prototype + + ''' def __init__(self): self.is_updated_geometry = False self.is_updated_transform = False self.is_updated_shading = False - self.is_updated_lightfilters = False + self.do_clear_instances = True class RmanSceneSync(object): ''' @@ -272,7 +285,7 @@ def check_particle_instancer(self, ob_update, psys): def update_particle_emitter(self, ob, psys, delete=False): psys_translator = self.rman_scene.rman_translators['PARTICLES'] proto_key = object_utils.prototype_key(ob) - rman_sg_particles = self.rman_scene.get_rman_particles(proto_key, psys, ob, create=delete) + rman_sg_particles = self.rman_scene.get_rman_particles(proto_key, psys, ob, create=not delete) if rman_sg_particles: psys_translator.update(ob, psys, rman_sg_particles) @@ -386,16 +399,18 @@ def check_object_datablock(self, obj): self.light_filter_updated(obj) elif rman_type == 'CAMERA': self.camera_updated(obj) - else: - if obj.id.original not in self.rman_updates: - rfb_log().debug("\tObject: %s Updated" % obj.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + else: + rfb_log().debug("\tObject: %s Updated" % obj.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) + if obj.id.original not in self.rman_updates: rman_update = RmanUpdate() rman_update.is_updated_geometry = obj.is_updated_geometry rman_update.is_updated_shading = obj.is_updated_shading rman_update.is_updated_transform = obj.is_updated_transform + if not rman_update.is_updated_geometry: + rman_update.do_clear_instances = False self.rman_updates[obj.id.original] = rman_update for psys in ob_eval.particle_systems: @@ -515,7 +530,7 @@ def batch_update_scene(self, context, depsgraph): rfb_log().debug("Set check_all_instances to True") self.check_all_instances = True - self.check_instances_batch() + self.check_instances(batch_mode=True) # update any materials for id, rman_sg_material in self.rman_scene.rman_materials.items(): @@ -530,96 +545,7 @@ def batch_update_scene(self, context, depsgraph): if self.rman_scene.do_motion_blur and self.rman_scene.moving_objects: self.rman_scene.export_instances_motion() - - @time_this - def check_instances_batch(self): - already_udpated = list() # list of objects already updated during our loop - clear_instances = list() # list of objects who had their instances cleared - rfb_log().debug("Updating instances") - - for instance in self.rman_scene.depsgraph.object_instances: - if instance.object.type in ('ARMATURE', 'CAMERA'): - continue - ob_key = instance.object.original - ob_eval = instance.object.evaluated_get(self.rman_scene.depsgraph) - instance_parent = None - psys = None - proto_key = object_utils.prototype_key(instance) - if instance.is_instance: - ob_key = instance.instance_object.original - psys = instance.particle_system - instance_parent = instance.parent - - if self.rman_scene.do_motion_blur: - if ob_key.name_full in self.rman_scene.moving_objects: - continue - - rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.rman_scene.get_rman_prototype(proto_key, ob=ob_eval) - if not rman_sg_node: - continue - - if self.check_all_instances: - # check all instances in the scene - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: - rman_update = RmanUpdate() - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - if rman_sg_node.is_frame_sensitive: - rman_update.is_updated_geometry = True - self.rman_updates[ob_key] = rman_update - else: - if ob_key not in self.rman_updates: - if rman_sg_node.is_frame_sensitive: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = True - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update - else: - if not instance_parent: - continue - if instance_parent.original not in self.rman_updates: - continue - rman_update = self.rman_updates[instance_parent.original] - else: - rman_update = self.rman_updates[ob_key] - - if rman_sg_node and not instance.is_instance: - if rman_update.is_updated_geometry and proto_key not in already_udpated: - translator = self.rman_scene.rman_translators.get(rman_type, None) - rfb_log().debug("\tUpdating Object: %s" % proto_key) - translator.update(ob_eval, rman_sg_node) - rman_sg_node.shared_attrs.Clear() - self.update_particle_emitters(ob_eval) - already_udpated.append(proto_key) - - if rman_type == 'EMPTY': - self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) - - if rman_type in object_utils._RMAN_NO_INSTANCES_: - continue - - # clear all instances for this prototype, if - # we have not already done so - if instance_parent: - parent_proto_key = object_utils.prototype_key(instance_parent) - rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) - if rman_parent_node and rman_parent_node not in clear_instances: - pass - ## Not sure if we need to do this? - - #rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) - #rman_parent_node.clear_instances() - #clear_instances.append(rman_parent_node) - elif rman_sg_node not in clear_instances: - rfb_log().debug("\tClearing instances: %s" % proto_key) - rman_sg_node.clear_instances() - clear_instances.append(rman_sg_node) - - self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) - + @time_this def update_scene(self, context, depsgraph): @@ -712,7 +638,7 @@ def update_scene(self, context, depsgraph): rfb_log().debug("------End update scene----------") @time_this - def check_instances(self): + def check_instances(self, batch_mode=False): deleted_obj_keys = list(self.rman_scene.rman_prototypes) # list of potential objects to delete already_udpated = list() # list of objects already updated during our loop clear_instances = list() # list of objects who had their instances cleared @@ -727,15 +653,21 @@ def check_instances(self): instance_parent = None psys = None is_new_object = False - proto_key = object_utils.prototype_key(instance) + proto_key = object_utils.prototype_key(instance) + is_empty_instancer = False if instance.is_instance: ob_key = instance.instance_object.original psys = instance.particle_system instance_parent = instance.parent + is_empty_instancer = (object_utils._detect_primitive_(instance_parent) == 'EMPTY_INSTANCER') - if proto_key in deleted_obj_keys: + if not batch_mode and proto_key in deleted_obj_keys: deleted_obj_keys.remove(proto_key) + if batch_mode and self.rman_scene.do_motion_blur: + if ob_key.name_full in self.rman_scene.moving_objects: + continue + rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) @@ -813,57 +745,70 @@ def check_instances(self): if rman_type in object_utils._RMAN_NO_INSTANCES_: continue - # clear all instances for this prototype, if - # we have not already done so - if instance_parent: - parent_proto_key = object_utils.prototype_key(instance_parent) - rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) - if rman_parent_node and rman_parent_node not in clear_instances: - pass - ## Not sure if we need to do this? - - #rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) - #rman_parent_node.clear_instances() - #clear_instances.append(rman_parent_node) - if rman_sg_node not in clear_instances: - rfb_log().debug("\tClearing instances: %s" % proto_key) - rman_sg_node.clear_instances() - clear_instances.append(rman_sg_node) - - if not self.rman_scene.check_visibility(instance): - # This instance is not visible in the viewport. Don't - # add an instance of it - continue - - self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) + if rman_update.do_clear_instances: + # clear all instances for this prototype, if + # we have not already done so + if psys and instance_parent: + parent_proto_key = object_utils.prototype_key(instance_parent) + rman_parent_node = self.rman_scene.get_rman_prototype(parent_proto_key, ob=instance_parent) + if rman_parent_node and rman_parent_node not in clear_instances: + rfb_log().debug("\tClearing parent instances: %s" % parent_proto_key) + rman_parent_node.clear_instances() + clear_instances.append(rman_parent_node) + if rman_sg_node not in clear_instances: + rfb_log().debug("\tClearing instances: %s" % proto_key) + rman_sg_node.clear_instances() + clear_instances.append(rman_sg_node) + + if not self.rman_scene.check_visibility(instance): + # This instance is not visible in the viewport. Don't + # add an instance of it + continue - if rman_type == 'LIGHT': - # We are dealing with a light. Check if it's a solo light, or muted - self.rman_scene.check_solo_light(rman_sg_node, ob_eval) + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) + else: + # simply grab the existing instance and update the transform and/or material + rman_sg_group = self.rman_scene.get_rman_sg_instance(instance, rman_sg_node, instance_parent, psys, create=False) + if rman_sg_group: + if rman_update.is_updated_transform: + rman_group_translator = self.rman_scene.rman_translators['GROUP'] + rman_group_translator.update_transform(instance, rman_sg_group) + if is_empty_instancer and instance_parent.renderman.rman_material_override: + self.rman_scene.attach_material(instance_parent, rman_sg_group) + elif rman_update.is_updated_shading: + if psys: + self.rman_scene.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) + else: + self.attach_material(ob_eval, rman_sg_group) + + if not batch_mode: + if rman_type == 'LIGHT': + # We are dealing with a light. Check if it's a solo light, or muted + self.rman_scene.check_solo_light(rman_sg_node, ob_eval) - # check portal lights - self.update_portals(ob_eval) - - # Hide the default light - if self.rman_scene.default_light.GetHidden() != 1: - self.rman_scene.default_light.SetHidden(1) - - # Delete any removed partcle systems - if proto_key in self.rman_scene.rman_particles: - ob_psys = self.rman_scene.rman_particles[proto_key] - rman_particle_nodes = list(ob_psys) - for psys in ob_eval.particle_systems: - try: - rman_particle_nodes.remove(psys.settings.original) - except: - continue - if rman_particle_nodes: - rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) - for k in rman_particle_nodes: - del ob_psys[k] + # check portal lights + self.update_portals(ob_eval) + + # Hide the default light + if self.rman_scene.default_light.GetHidden() != 1: + self.rman_scene.default_light.SetHidden(1) + + # Delete any removed partcle systems + if proto_key in self.rman_scene.rman_particles: + ob_psys = self.rman_scene.rman_particles[proto_key] + rman_particle_nodes = list(ob_psys) + for psys in ob_eval.particle_systems: + try: + rman_particle_nodes.remove(psys.settings.original) + except: + continue + if rman_particle_nodes: + rfb_log().debug("\t\tRemoving particle nodes: %s" % proto_key) + for k in rman_particle_nodes: + del ob_psys[k] # delete objects - if deleted_obj_keys: + if not batch_mode and deleted_obj_keys: self.delete_objects(deleted_obj_keys) @time_this From c7c8509cd1964f9096c9a97b85f166f92c3c5182 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 10:59:04 -0700 Subject: [PATCH 137/278] Some more code clean up in rman_scene_sync. * renamed variables to make it clear what they are * rearranged some of the conditionals in check_instances --- rfb_utils/object_utils.py | 3 + rman_scene.py | 4 +- rman_scene_sync.py | 227 +++++++++++++++++++------------------- 3 files changed, 120 insertions(+), 114 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 9abd05cd..40b4ccf7 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -63,6 +63,9 @@ def is_portal_light(ob): rm = ob.data.renderman return (rm.renderman_light_role == 'RMAN_LIGHT' and rm.get_light_node_name() == 'PxrPortalLight') +def is_empty_instancer(ob): + return (_detect_primitive_(ob) == 'EMPTY_INSTANCER') + def is_particle_instancer(psys, particle_settings=None): psys_settings = particle_settings if not psys_settings: diff --git a/rman_scene.py b/rman_scene.py index 331ae9e0..fe443fc5 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -625,8 +625,8 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa rman_group_translator = self.rman_translators['GROUP'] rman_sg_group = self.get_rman_sg_instance(ob_inst, rman_sg_node, instance_parent, psys, create=True) is_empty_instancer = False - if instance_parent and object_utils._detect_primitive_(instance_parent) == 'EMPTY_INSTANCER': - is_empty_instancer = True + if instance_parent: + is_empty_instancer = object_utils.is_empty_instancer(instance_parent) # Object attrs translator = self.rman_translators.get(rman_type, None) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 63f04dc5..a6eb31be 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -164,11 +164,11 @@ def _mesh_light_update(self, mat): for ob in object_list: ob.update_tag() - def material_updated(self, obj, rman_sg_material=None): - if isinstance(obj, bpy.types.DepsgraphUpdate): - mat = obj.id + def material_updated(self, ob_update, rman_sg_material=None): + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + mat = ob_update.id else: - mat = obj + mat = ob_update if rman_sg_material is None: rman_sg_material = self.rman_scene.rman_materials.get(mat.original, None) translator = self.rman_scene.rman_translators["MATERIAL"] @@ -195,26 +195,31 @@ def light_filter_transform_updated(self, ob, rman_sg_lightfilter): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_group_translator.update_transform(ob, rman_sg_lightfilter) - def light_filter_updated(self, obj, force_update=False): - if isinstance(obj, bpy.types.DepsgraphUpdate): - ob = obj.id.evaluated_get(self.rman_scene.depsgraph) + def light_filter_updated(self, ob_update, force_update=False): + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + ob = ob_update.id.evaluated_get(self.rman_scene.depsgraph) else: - ob = obj.evaluated_get(self.rman_scene.depsgraph) + ob = ob_update.evaluated_get(self.rman_scene.depsgraph) proto_key = object_utils.prototype_key(ob) rman_sg_lightfilter = self.rman_scene.get_rman_prototype(proto_key) if not rman_sg_lightfilter: # Light filter needs to be added rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + else: + rman_update.is_updated_geometry = True + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True self.rman_updates[ob.original] = rman_update return - if force_update or obj.is_updated_transform or obj.is_updated_shading: + if force_update or ob_update.is_updated_transform or ob_update.is_updated_shading: rfb_log().debug("\tLight Filter: %s Transform Updated" % ob.name) self.light_filter_transform_updated(ob, rman_sg_lightfilter) - if force_update or obj.is_updated_geometry: + if force_update or ob_update.is_updated_geometry: rfb_log().debug("\tLight Filter: %s Shading Updated" % ob.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.rman_translators['LIGHTFILTER'].update(ob, rman_sg_lightfilter) @@ -295,7 +300,10 @@ def update_particle_emitters(self, ob): self.update_particle_emitter(ob, psys) def update_empty(self, ob_update, rman_sg_node=None): - ob = ob_update.id + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + ob = ob_update.id + else: + ob = ob_update.evaluated_get(self.rman_scene.depsgraph) rfb_log().debug("Update empty: %s" % ob.name) if ob.is_instancer: rfb_log().debug("\tEmpty is an instancer") @@ -307,9 +315,14 @@ def update_empty(self, ob_update, rman_sg_node=None): if col_obj.original in self.rman_updates: continue rman_update = RmanUpdate() - rman_update.is_updated_geometry = ob_update.is_updated_geometry - rman_update.is_updated_shading = ob_update.is_updated_shading - rman_update.is_updated_transform = ob_update.is_updated_transform + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + rman_update.is_updated_geometry = ob_update.is_updated_geometry + rman_update.is_updated_shading = ob_update.is_updated_shading + rman_update.is_updated_transform = ob_update.is_updated_transform + else: + rman_update.is_updated_geometry = True + rman_update.is_updated_shading = True + rman_update.is_updated_transform = True self.rman_updates[col_obj.original] = rman_update else: rfb_log().debug("\tRegular empty") @@ -361,11 +374,11 @@ def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): portal.original.update_tag() - def check_light_datablock(self, obj): - if isinstance(obj, bpy.types.DepsgraphUpdate): - ob = obj.id.original + def check_light_datablock(self, ob_update): + if isinstance(ob_update, bpy.types.DepsgraphUpdate): + ob = ob_update.id.original else: - ob = obj.original + ob = ob_update.original users = self.rman_scene.context.blend_data.user_map(subset={ob}, value_types={'OBJECT'}) for o in users[ob]: rman_type = object_utils._detect_primitive_(o) @@ -385,8 +398,8 @@ def check_focus_object(self, ob): for o in users[camera]: self.camera_updated(o.original, force_update=True) - def check_object_datablock(self, obj): - ob_eval = obj.id.evaluated_get(self.rman_scene.depsgraph) + def check_object_datablock(self, dps_update): + ob_eval = dps_update.id.evaluated_get(self.rman_scene.depsgraph) rman_type = object_utils._detect_primitive_(ob_eval) if ob_eval.type in ('ARMATURE'): @@ -394,44 +407,44 @@ def check_object_datablock(self, obj): # These types need special handling if rman_type == 'EMPTY': - self.update_empty(obj) + self.update_empty(dps_update) elif rman_type == 'LIGHTFILTER': - self.light_filter_updated(obj) + self.light_filter_updated(dps_update) elif rman_type == 'CAMERA': - self.camera_updated(obj) + self.camera_updated(dps_update) else: - rfb_log().debug("\tObject: %s Updated" % obj.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(obj.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(obj.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(obj.is_updated_transform)) - if obj.id.original not in self.rman_updates: + rfb_log().debug("\tObject: %s Updated" % dps_update.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(dps_update.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(dps_update.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(dps_update.is_updated_transform)) + if dps_update.id.original not in self.rman_updates: rman_update = RmanUpdate() - rman_update.is_updated_geometry = obj.is_updated_geometry - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform + rman_update.is_updated_geometry = dps_update.is_updated_geometry + rman_update.is_updated_shading = dps_update.is_updated_shading + rman_update.is_updated_transform = dps_update.is_updated_transform if not rman_update.is_updated_geometry: rman_update.do_clear_instances = False - self.rman_updates[obj.id.original] = rman_update + self.rman_updates[dps_update.id.original] = rman_update for psys in ob_eval.particle_systems: if object_utils.is_particle_instancer(psys): - self.check_particle_instancer(obj, psys) + self.check_particle_instancer(dps_update, psys) # Check if this object is the focus object the camera. If it is # we need to update the camera - if obj.is_updated_transform: + if dps_update.is_updated_transform: self.check_focus_object(ob_eval) - def check_particle_settings(self, obj): - rfb_log().debug("ParticleSettings updated: %s" % obj.id.name) + def check_particle_settings(self, dps_update): + rfb_log().debug("ParticleSettings updated: %s" % dps_update.id.name) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - users = self.rman_scene.context.blend_data.user_map(subset={obj.id.original}, value_types={'OBJECT'}) - for o in users[obj.id.original]: + users = self.rman_scene.context.blend_data.user_map(subset={dps_update.id.original}, value_types={'OBJECT'}) + for o in users[dps_update.id.original]: ob = o.evaluated_get(self.rman_scene.depsgraph) psys = None for ps in ob.particle_systems: - if ps.settings.original == obj.id.original: + if ps.settings.original == dps_update.id.original: psys = ps break if not psys: @@ -445,21 +458,21 @@ def check_particle_settings(self, obj): if o.original not in self.rman_updates: rman_update = RmanUpdate() - rman_update.is_updated_shading = obj.is_updated_shading - rman_update.is_updated_transform = obj.is_updated_transform + rman_update.is_updated_shading = dps_update.is_updated_shading + rman_update.is_updated_transform = dps_update.is_updated_transform self.rman_updates[o.original] = rman_update - def check_shader_nodetree(self, obj): - if obj.id.name in bpy.data.node_groups: - if len(obj.id.nodes) < 1: + def check_shader_nodetree(self, dps_update): + if dps_update.id.name in bpy.data.node_groups: + if len(dps_update.id.nodes) < 1: return - if not obj.id.name.startswith(rman_constants.RMAN_FAKE_NODEGROUP): + if not dps_update.id.name.startswith(rman_constants.RMAN_FAKE_NODEGROUP): return # this is one of our fake node groups with ramps # update all of the users of this node tree - rfb_log().debug("ShaderNodeTree updated: %s" % obj.id.name) - users = self.rman_scene.context.blend_data.user_map(subset={obj.id.original}) - for o in users[obj.id.original]: + rfb_log().debug("ShaderNodeTree updated: %s" % dps_update.id.name) + users = self.rman_scene.context.blend_data.user_map(subset={dps_update.id.original}) + for o in users[dps_update.id.original]: if self.rman_scene.is_interactive: if hasattr(o, 'rman_nodetree'): o.rman_nodetree.update_tag() @@ -501,27 +514,25 @@ def batch_update_scene(self, context, depsgraph): self.rman_scene.num_object_instances = len(depsgraph.object_instances) with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - for obj in reversed(depsgraph.updates): - ob = obj.id - - if isinstance(obj.id, bpy.types.ParticleSettings): - self.check_particle_settings(obj) + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.ParticleSettings): + self.check_particle_settings(dps_update) - elif isinstance(obj.id, bpy.types.Light): - self.check_light_datablock(obj) + elif isinstance(dps_update.id, bpy.types.Light): + self.check_light_datablock(dps_update) - elif isinstance(obj.id, bpy.types.Material): - rfb_log().debug("Material updated: %s" % obj.id.name) - ob = obj.id + elif isinstance(dps_update.id, bpy.types.Material): + rfb_log().debug("Material updated: %s" % dps_update.id.name) + ob = dps_update.id if ob.original not in self.rman_updates: rman_update = RmanUpdate() self.rman_updates[ob.original] = rman_update - elif isinstance(obj.id, bpy.types.ShaderNodeTree): - self.check_shader_nodetree(obj) + elif isinstance(dps_update.id, bpy.types.ShaderNodeTree): + self.check_shader_nodetree(dps_update) - elif isinstance(obj.id, bpy.types.Object): - self.check_object_datablock(obj) + elif isinstance(dps_update.id, bpy.types.Object): + self.check_object_datablock(dps_update) if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able @@ -567,23 +578,21 @@ def update_scene(self, context, depsgraph): self.num_instances_changed = True self.rman_scene.num_object_instances = len(depsgraph.object_instances) - for obj in reversed(depsgraph.updates): - ob = obj.id - - if isinstance(obj.id, bpy.types.Scene): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Scene): self.scene_updated() - elif isinstance(obj.id, bpy.types.World): + elif isinstance(dps_update.id, bpy.types.World): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): self.rman_scene.export_integrator() self.rman_scene.export_samplefilters() self.rman_scene.export_displayfilters() self.rman_scene.export_viewport_stats() - elif isinstance(obj.id, bpy.types.Camera): - rfb_log().debug("Camera updated: %s" % obj.id.name) + elif isinstance(dps_update.id, bpy.types.Camera): + rfb_log().debug("Camera updated: %s" % dps_update.id.name) if self.rman_scene.is_viewport_render: - if self.rman_scene.bl_scene.camera.data != obj.id: + if self.rman_scene.bl_scene.camera.data != dps_update.id: continue rman_sg_camera = self.rman_scene.main_camera translator = self.rman_scene.rman_translators['CAMERA'] @@ -593,35 +602,35 @@ def update_scene(self, context, depsgraph): translator = self.rman_scene.rman_translators['CAMERA'] with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): for ob, rman_sg_camera in self.rman_scene.rman_cameras.items(): - if ob.original.name != obj.id.name: + if ob.original.name != dps_update.id.name: continue translator._update_render_cam(ob.original, rman_sg_camera) - elif isinstance(obj.id, bpy.types.Material): - rfb_log().debug("Material updated: %s" % obj.id.name) - self.material_updated(obj) + elif isinstance(dps_update.id, bpy.types.Material): + rfb_log().debug("Material updated: %s" % dps_update.id.name) + self.material_updated(dps_update) - elif isinstance(obj.id, bpy.types.Mesh): - rfb_log().debug("Mesh updated: %s" % obj.id.name) + elif isinstance(dps_update.id, bpy.types.Mesh): + rfb_log().debug("Mesh updated: %s" % dps_update.id.name) - elif isinstance(obj.id, bpy.types.Light): - self.check_light_datablock(obj) + elif isinstance(dps_update.id, bpy.types.Light): + self.check_light_datablock(dps_update) - elif isinstance(obj.id, bpy.types.ParticleSettings): - self.check_particle_settings(obj) + elif isinstance(dps_update.id, bpy.types.ParticleSettings): + self.check_particle_settings(dps_update) - elif isinstance(obj.id, bpy.types.ShaderNodeTree): - self.check_shader_nodetree(obj) + elif isinstance(dps_update.id, bpy.types.ShaderNodeTree): + self.check_shader_nodetree(dps_update) - elif isinstance(obj.id, bpy.types.Object): - self.check_object_datablock(obj) + elif isinstance(dps_update.id, bpy.types.Object): + self.check_object_datablock(dps_update) - elif isinstance(obj.id, bpy.types.Collection): - rfb_log().debug("Collection updated: %s" % obj.id.name) - #self.update_collection(obj.id) + elif isinstance(dps_update.id, bpy.types.Collection): + rfb_log().debug("Collection updated: %s" % dps_update.id.name) + #self.update_collection(dps_update.id) else: - rfb_log().debug("Not handling %s update: %s" % (str(type(obj.id)), obj.id.name)) + rfb_log().debug("Not handling %s update: %s" % (str(type(dps_update.id)), dps_update.id.name)) if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able @@ -659,15 +668,11 @@ def check_instances(self, batch_mode=False): ob_key = instance.instance_object.original psys = instance.particle_system instance_parent = instance.parent - is_empty_instancer = (object_utils._detect_primitive_(instance_parent) == 'EMPTY_INSTANCER') + is_empty_instancer = object_utils.is_empty_instancer(instance_parent) - if not batch_mode and proto_key in deleted_obj_keys: + if proto_key in deleted_obj_keys: deleted_obj_keys.remove(proto_key) - - if batch_mode and self.rman_scene.do_motion_blur: - if ob_key.name_full in self.rman_scene.moving_objects: - continue - + rman_type = object_utils._detect_primitive_(ob_eval) rman_sg_node = self.rman_scene.get_rman_prototype(proto_key) @@ -682,6 +687,7 @@ def check_instances(self, batch_mode=False): del self.rman_scene.rman_prototypes[proto_key] rman_sg_node = None + rman_update = self.rman_updates.get(ob_key, None) if not rman_sg_node: # this is a new object. rman_sg_node = self.rman_scene.export_data_block(proto_key, ob_eval) @@ -699,8 +705,7 @@ def check_instances(self, batch_mode=False): continue is_new_object = True - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: + if rman_update is None: rman_update = RmanUpdate() rman_update.is_updated_geometry = True rman_update.is_updated_shading = True @@ -708,27 +713,26 @@ def check_instances(self, batch_mode=False): self.rman_updates[ob_key] = rman_update rman_update.is_updated_geometry = False clear_instances.append(rman_sg_node) - + if self.check_all_instances: # check all instances in the scene - rman_update = self.rman_updates.get(ob_key, None) - if not rman_update: + # build an RmanUpdate instance if it doesn't exist + if rman_update is None: rman_update = RmanUpdate() rman_update.is_updated_shading = True rman_update.is_updated_transform = True if rman_sg_node.is_frame_sensitive and self.frame_number_changed: rman_update.is_updated_geometry = True self.rman_updates[ob_key] = rman_update - elif ob_key in self.rman_updates: - rman_update = self.rman_updates[ob_key] - else: + elif rman_update is None: # check if the instance_parent was the thing that # changed if not instance_parent: continue - if instance_parent.original not in self.rman_updates: + rman_update = self.rman_updates.get(instance_parent.original, None) + if rman_update is None: continue - rman_update = self.rman_updates[instance_parent.original] + rfb_log().debug("\t%s parent updated (%s)" % (ob_eval.name, instance_parent.name)) if rman_sg_node and not is_new_object and not instance.is_instance: if rman_update.is_updated_geometry and proto_key not in already_udpated: @@ -739,10 +743,9 @@ def check_instances(self, batch_mode=False): self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) - if rman_type == 'EMPTY': - self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) - if rman_type in object_utils._RMAN_NO_INSTANCES_: + if rman_type == 'EMPTY': + self.rman_scene._export_hidden_instance(ob_eval, rman_sg_node) continue if rman_update.do_clear_instances: @@ -808,7 +811,7 @@ def check_instances(self, batch_mode=False): del ob_psys[k] # delete objects - if not batch_mode and deleted_obj_keys: + if deleted_obj_keys: self.delete_objects(deleted_obj_keys) @time_this From 9c27cf65a840376f33a5a99f1d044b92e3b4679f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 11:47:14 -0700 Subject: [PATCH 138/278] Only create one node group to hold our fake_nodegroup with all of our ramp nodes. --- rman_bl_nodes/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index ef0c9a5e..3ecfa33e 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -362,8 +362,11 @@ def init(self, context): float_rman_ramps = self.__annotations__.get('__FLOAT_RAMPS__', []) if color_rman_ramps or float_rman_ramps: - node_group = bpy.data.node_groups.new( - RMAN_FAKE_NODEGROUP, 'ShaderNodeTree') + if RMAN_FAKE_NODEGROUP in bpy.data.node_groups: + node_group = bpy.data.node_groups[RMAN_FAKE_NODEGROUP] + else: + node_group = bpy.data.node_groups.new( + RMAN_FAKE_NODEGROUP, 'ShaderNodeTree') node_group.use_fake_user = True self.rman_fake_node_group_ptr = node_group self.rman_fake_node_group = node_group.name From 046247ea3ef75c6441c1c6ac04ec6e4e3ed76f2e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 12:37:16 -0700 Subject: [PATCH 139/278] Fix the problem where ramps don't update in IPR when they are first added. For some reason, when ramps are added but before the scene is saved, edits to ramps will trigger an view_update callback, but depsgraph.updates is empty. If we're in this situation, assume we want a nodetree to be updated. --- rman_scene_sync.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a6eb31be..e75530c9 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -569,6 +569,33 @@ def update_scene(self, context, depsgraph): self.rman_scene.bl_scene = depsgraph.scene self.rman_scene.context = context + if len(depsgraph.updates) < 1: + # Updates is empty?! This seems like a Blender bug. + # We seem to get into this situation when ramps are being edited, but the scene + # has not been saved since the ramp was added. + space = getattr(bpy.context, 'space_data', None) + rfb_log().debug("------Start update scene--------") + rfb_log().debug("DepsgraphUpdates is empty. Assume this is a material edit.") + node_tree = None + if space and space.type == 'NODE_EDITOR': + node_tree = space.node_tree + if isinstance(node_tree, bpy.types.ShaderNodeTree): + node_tree.update_tag() + else: + node_tree = None + if node_tree is None and context.view_layer: + # The current space doesn't seem to be the shader editor. + # Fallback to looking for the active object + ob = context.view_layer.objects.active + if hasattr(ob, 'active_material'): + ob.active_material.node_tree.update_tag() + elif hasattr(ob, 'rman_nodetree'): + ob.rman_nodetree.update_tag() + elif ob.type == 'LIGHT': + ob.data.node_tree.update_tag() + + rfb_log().debug("------End update scene----------") + rfb_log().debug("------Start update scene--------") # Check the number of instances. If we differ, an object may have been From f3ce735a3aea0de3234cacfc0b5332aaf88add5a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 12:58:25 -0700 Subject: [PATCH 140/278] Revert the previous change regarding trying to use one node_group for all ramps. Need to think about it some more, as we don't want to edit all things that have ramps, if only one of the ramp nodes changed. --- rman_bl_nodes/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index 3ecfa33e..ef0c9a5e 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -362,11 +362,8 @@ def init(self, context): float_rman_ramps = self.__annotations__.get('__FLOAT_RAMPS__', []) if color_rman_ramps or float_rman_ramps: - if RMAN_FAKE_NODEGROUP in bpy.data.node_groups: - node_group = bpy.data.node_groups[RMAN_FAKE_NODEGROUP] - else: - node_group = bpy.data.node_groups.new( - RMAN_FAKE_NODEGROUP, 'ShaderNodeTree') + node_group = bpy.data.node_groups.new( + RMAN_FAKE_NODEGROUP, 'ShaderNodeTree') node_group.use_fake_user = True self.rman_fake_node_group_ptr = node_group self.rman_fake_node_group = node_group.name From b58b519475a84856fbead1f91216101c9d4f9733 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 13:07:13 -0700 Subject: [PATCH 141/278] Need to add some extra error checking in our empty depsgraph updates code path. --- rman_scene_sync.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index e75530c9..a789a235 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -587,13 +587,14 @@ def update_scene(self, context, depsgraph): # The current space doesn't seem to be the shader editor. # Fallback to looking for the active object ob = context.view_layer.objects.active - if hasattr(ob, 'active_material'): - ob.active_material.node_tree.update_tag() - elif hasattr(ob, 'rman_nodetree'): - ob.rman_nodetree.update_tag() - elif ob.type == 'LIGHT': - ob.data.node_tree.update_tag() - + if ob: + if hasattr(ob, 'active_material') and ob.active_material: + ob.active_material.node_tree.update_tag() + elif hasattr(ob, 'rman_nodetree'): + ob.rman_nodetree.update_tag() + elif ob.type == 'LIGHT': + ob.data.node_tree.update_tag() + rfb_log().debug("------End update scene----------") rfb_log().debug("------Start update scene--------") From 6f731f9c16d7483ddd2abd5c8f76934f62ef7434 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 14:20:28 -0700 Subject: [PATCH 142/278] Fix issue where materials with keyframed parameters were not updating correctly when persisten_data was on. Don't use the .original version of the material in the material translator. We want the evaluated version. --- rman_scene_sync.py | 2 +- rman_translators/rman_material_translator.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a789a235..a6ca673f 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -546,7 +546,7 @@ def batch_update_scene(self, context, depsgraph): # update any materials for id, rman_sg_material in self.rman_scene.rman_materials.items(): if rman_sg_material.is_frame_sensitive or id.original in self.rman_updates: - mat = id.original + mat = id.evaluated_get(self.rman_scene.depsgraph) self.material_updated(mat, rman_sg_material) self.rman_scene.export_integrator() diff --git a/rman_translators/rman_material_translator.py b/rman_translators/rman_material_translator.py index 6496efe8..e7cc8401 100644 --- a/rman_translators/rman_material_translator.py +++ b/rman_translators/rman_material_translator.py @@ -41,7 +41,6 @@ def export(self, mat, db_name): def update(self, mat, rman_sg_material, time_sample=0): - mat = mat.original rm = mat.renderman succeed = False From b21642ee74e513e953ff78c9cb11750745a462e3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 2 Jun 2022 15:26:49 -0700 Subject: [PATCH 143/278] Add an extra check when depsgraph updates is empty to only update if what was updated was a nodetree. --- rman_scene_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a6ca673f..e144e8b7 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -569,7 +569,7 @@ def update_scene(self, context, depsgraph): self.rman_scene.bl_scene = depsgraph.scene self.rman_scene.context = context - if len(depsgraph.updates) < 1: + if len(depsgraph.updates) < 1 and depsgraph.id_type_updated('NODETREE'): # Updates is empty?! This seems like a Blender bug. # We seem to get into this situation when ramps are being edited, but the scene # has not been saved since the ramp was added. From a0d96f5d3903a930d6e32931b580283f322fea4f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Jun 2022 08:04:01 -0700 Subject: [PATCH 144/278] For camera edits, make sure we add an RmanUpdate instance. Also, make sure we still do camera transforms for non main cameras during viewport rendering. --- rman_scene_sync.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index e144e8b7..b98af960 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -240,6 +240,9 @@ def camera_updated(self, ob_update, force_update=False): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): rman_sg_camera = self.rman_scene.rman_cameras.get(ob.original) translator = self.rman_scene.rman_translators['CAMERA'] + + rman_update = RmanUpdate() + self.rman_updates[ob.original] = rman_update if not rman_sg_camera: rfb_log().debug("\tNew Camera: %s" % ob.name) @@ -260,7 +263,7 @@ def camera_updated(self, ob_update, force_update=False): if force_update or ob_update.is_updated_transform: # we deal with main camera transforms in view_draw - if self.rman_scene.is_viewport_render: + if self.rman_scene.is_viewport_render and self.rman_scene.main_camera == rman_sg_camera: return if translator._update_render_cam_transform(ob, rman_sg_camera): rfb_log().debug("\tCamera Transform Updated: %s" % ob.name) @@ -501,7 +504,7 @@ def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_scene = depsgraph.scene_eval self.rman_scene.context = context - # updat the frame number + # update the frame number options = self.rman_scene.sg_scene.GetOptions() options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.rman_scene.bl_frame_current) self.rman_scene.sg_scene.SetOptions(options) From 680b604ae96f321ff2ed1b0dcd1fa23546538c98 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Jun 2022 13:51:25 -0700 Subject: [PATCH 145/278] Re-work array connections between shading nodes. * add separate output sockets to a node, if an output parameter is an array * if we directly connect to the array output param, we use its size as the array length. --- rfb_utils/property_utils.py | 30 ++++++++++++++++++++------ rfb_utils/rman_socket_utils.py | 14 +++++++++++- rman_bl_nodes/__init__.py | 4 ++++ rman_bl_nodes/rman_bl_nodes_sockets.py | 8 +++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 8b652f75..39040ccd 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -645,12 +645,14 @@ def set_array_rixparams(node, rman_sg_node, mat_name, bl_prop_info, prop_name, p param_type = bl_prop_info.renderman_array_type param_name = bl_prop_info.renderman_name collection = getattr(node, coll_nm) - - for i in range(len(collection)): + any_connections = False + inputs = getattr(node, 'inputs', dict()) + input_array_size = len(collection) + for i in range(input_array_size): elem = collection[i] nm = '%s[%d]' % (prop_name, i) - if hasattr(node, 'inputs') and nm in node.inputs and \ - node.inputs[nm].is_linked: + if nm in node.inputs and inputs[nm].is_linked: + any_connections = True to_socket = node.inputs[nm] from_socket = to_socket.links[0].from_socket from_node = to_socket.links[0].from_node @@ -658,15 +660,29 @@ def set_array_rixparams(node, rman_sg_node, mat_name, bl_prop_info, prop_name, p val = get_output_param_str(rman_sg_node, from_node, mat_name, from_socket, to_socket, param_type) if val: - val_ref_array.append(val) + if getattr(from_socket, 'is_array', False): + # the socket from the incoming connection is an array + # clear val_ref_array so far and resize it to this + # socket's array size + array_size = from_socket.array_size + val_ref_array.clear() + for i in range(array_size): + cnx_val = '%s[%d]' % (val, i) + val_ref_array.append(cnx_val) + break + val_ref_array.append(val) + else: + val_ref_array.append("") else: prop = getattr(elem, 'value_%s' % param_type) val = string_utils.convert_val(prop, type_hint=param_type) if param_type in RFB_FLOAT3: val_array.extend(val) else: - val_array.append(val) - if val_ref_array: + val_array.append(val) + val_ref_array.append("") + + if any_connections: set_rix_param(params, param_type, param_name, val_ref_array, is_reference=True, is_array=True, array_len=len(val_ref_array)) else: set_rix_param(params, param_type, param_name, val_array, is_reference=False, is_array=True, array_len=len(val_array)) diff --git a/rfb_utils/rman_socket_utils.py b/rfb_utils/rman_socket_utils.py index 1105a500..9bb1394a 100644 --- a/rfb_utils/rman_socket_utils.py +++ b/rfb_utils/rman_socket_utils.py @@ -117,4 +117,16 @@ def node_add_outputs(node): socket.renderman_type = rman_type if rman_type == 'struct': struct_name = meta.get('struct_name', 'Manifold') - socket.struct_name = struct_name \ No newline at end of file + socket.struct_name = struct_name + + arraySize = meta['arraySize'] + if arraySize > 0: + # this is an array + # add separate scokets for each element + socket.is_array = True + socket.array_size = arraySize + for i in range(0, arraySize): + elem_nm = '%s[%d]' % (name, i) + elem_socket = node.outputs.new(__RMAN_SOCKET_MAP__[rman_type], elem_nm) + elem_socket.renderman_type = rman_type + elem_socket.array_elem = i \ No newline at end of file diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index ef0c9a5e..353469d1 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -266,6 +266,10 @@ def class_generate_properties(node, parent_name, node_desc): output_prop_meta['vstruct'] = True if hasattr(node_desc_param, 'struct_name'): output_prop_meta['struct_name'] = node_desc_param.struct_name + if node_desc_param.is_array(): + output_prop_meta['arraySize'] = node_desc_param.size + else: + output_prop_meta['arraySize'] = -1 output_prop_meta['name'] = node_desc_param.name output_meta[prop_name] = output_prop_meta output_meta[prop_name]['renderman_type'] = renderman_type diff --git a/rman_bl_nodes/rman_bl_nodes_sockets.py b/rman_bl_nodes/rman_bl_nodes_sockets.py index b1c0db42..6997cf08 100644 --- a/rman_bl_nodes/rman_bl_nodes_sockets.py +++ b/rman_bl_nodes/rman_bl_nodes_sockets.py @@ -249,6 +249,11 @@ def draw(self, context, layout, node, text): pass elif self.hide_value: layout.label(text=self.get_pretty_name(node)) + elif self.is_output: + if self.is_array: + layout.label(text='%s[]' % (self.get_pretty_name(node))) + else: + layout.label(text=self.get_pretty_name(node)) elif self.is_linked or self.is_output: layout.label(text=self.get_pretty_name(node)) elif node.bl_idname in __CYCLES_GROUP_NODES__ or node.bl_idname == "PxrOSLPatternNode": @@ -348,6 +353,9 @@ def draw_color(self, context, node): ann_dict = socket_info[5] for k, v in ann_dict.items(): ntype.__annotations__[k] = v + ntype.__annotations__['is_array'] = BoolProperty(default=False) + ntype.__annotations__['array_size'] = IntProperty(default=-1) + ntype.__annotations__['array_elem'] = IntProperty(default=-1) classes.append(ntype) From 06b1cd97e4ad4b15d660b2243604841c34e0afac Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Jun 2022 15:54:19 -0700 Subject: [PATCH 146/278] Add hider:bluenoise option --- rman_config/config/rman_properties_scene.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 9376f4af..c53bb8b4 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -780,6 +780,17 @@ "widget": "checkbox", "help": "Experimental scheme to use earlier lighting results to drive selection of lights in later iterations." }, + { + "panel": "RENDER_PT_renderman_advanced_settings", + "name": "hider_bluenoise", + "riopt": "hider:bluenoise", + "label": "Blue Noise Samples", + "type": "int", + "default": 1, + "widget": "checkbox", + "connectable": false, + "help": "Setting to control whether the samples should have a visually pleasing blue noise distribution or not. Default is 1 (on). Set to 0 (off) if the samples in adjacent pixels need to be completely decorrelated." + }, { "panel": "RENDER_PT_renderman_advanced_settings", "name": "shade_roughnessmollification", From 0d8b2aa6cecca1da5a7a13ec246c3858d51dc86f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Jun 2022 16:17:45 -0700 Subject: [PATCH 147/278] Forgot to pass the prop_name to set_rix_param, for export_root_sg_node and export_global_options. --- rman_scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index fe443fc5..be3cb17e 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -523,7 +523,7 @@ def export_root_sg_node(self): if type(val) == str and val.startswith('['): val = eval(val) param_type = meta['renderman_type'] - property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm) + property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) if rm.invert_light_linking: all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)] @@ -1152,7 +1152,7 @@ def export_global_options(self): param_type = meta['renderman_type'] if param_type == "string": val = string_utils.expand_string(val, asFilePath=True) - property_utils.set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm) + property_utils.set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) # threads if not self.external_render: From 1e84507aa2bc59b16f3b8e53a49edea9b044b4e8 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 6 Jun 2022 09:56:46 -0700 Subject: [PATCH 148/278] Some modifications to set_rix_param. * move some of the handling of converting the default values to the convert_val function in string_utils. * call expand_string in convert_val, when we are dealing with strings. --- rfb_utils/property_utils.py | 19 +------------------ rfb_utils/string_utils.py | 29 ++++++++++++++++++++++------- rman_scene.py | 13 ++++--------- rman_translators/rman_translator.py | 5 ++--- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 39040ccd..c325e3a1 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -190,24 +190,7 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar # not setting the value during IPR, if the user happens to change # the param val back to default. if dflt != None and not params.HasParam(param_name): - if isinstance(val, list): - dflt = list(dflt) - - if not is_array: - if ((isinstance(val, str) or isinstance(dflt, str))): - # these explicit conversions are necessary because of EnumProperties - if param_type == 'string' and val == __RMAN_EMPTY_STRING__: - val = "" - elif param_type == 'int': - val = int(val) - dflt = int(dflt) - elif param_type == 'float': - val = float(val) - dflt = float(dflt) - elif param_type == 'int' and (isinstance(val, bool) or isinstance(dflt, bool)): - # convert bools into ints - val = int(val) - dflt = int(dflt) + dflt = string_utils.convert_val(dflt, type_hint=param_type) # Check if this param is marked always_write. # We still have some plugins where the Args file and C++ don't agree diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 978db3e7..76c9ef3d 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -238,28 +238,43 @@ def _format_time_(seconds): def convert_val(v, type_hint=None): import mathutils + converted_val = v + # float, int if type_hint == 'color': - return list(v)[:3] + converted_val = list(v)[:3] if type(v) in (mathutils.Vector, mathutils.Color) or\ v.__class__.__name__ == 'bpy_prop_array'\ or v.__class__.__name__ == 'Euler': # BBM modified from if to elif - return list(v) + converted_val = list(v) + + elif type(v) == str and v.startswith('['): + converted_val = eval(v) + + elif type(v) == list: + converted_val = v # matrix elif type(v) == mathutils.Matrix: - return [v[0][0], v[1][0], v[2][0], v[3][0], + converted_val = [v[0][0], v[1][0], v[2][0], v[3][0], v[0][1], v[1][1], v[2][1], v[3][1], v[0][2], v[1][2], v[2][2], v[3][2], v[0][3], v[1][3], v[2][3], v[3][3]] elif type_hint == 'int': - return int(v) + converted_val = int(v) elif type_hint == 'float': - return float(v) - else: - return v + converted_val = float(v) + + if type_hint == 'string': + if isinstance(converted_val, list): + for i in range(len(converted_val)): + converted_val[i] = expand_string(converted_val[i], asFilePath=True) + else: + converted_val = expand_string(converted_val, asFilePath=True) + + return converted_val def getattr_recursive(ptr, attrstring): for attr in attrstring.split("."): diff --git a/rman_scene.py b/rman_scene.py index be3cb17e..02b9ea8b 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -519,10 +519,9 @@ def export_root_sg_node(self): array_len = -1 if 'arraySize' in meta: is_array = True - array_len = meta['arraySize'] - if type(val) == str and val.startswith('['): - val = eval(val) - param_type = meta['renderman_type'] + array_len = meta['arraySize'] + param_type = meta['renderman_type'] + val = string_utils.convert_val(val, type_hint=param_type) property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) if rm.invert_light_linking: @@ -1146,12 +1145,8 @@ def export_global_options(self): if 'arraySize' in meta: is_array = True array_len = meta['arraySize'] - if type(val) == str and val.startswith('['): - val = eval(val) - param_type = meta['renderman_type'] - if param_type == "string": - val = string_utils.expand_string(val, asFilePath=True) + val = string_utils.convert_val(val, type_hint=param_type) property_utils.set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) # threads diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 38a15f10..2d58879a 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -221,10 +221,9 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): array_len = -1 if 'arraySize' in meta: is_array = True - array_len = meta['arraySize'] - if type(val) == str and val.startswith('['): - val = eval(val) + array_len = meta['arraySize'] param_type = meta['renderman_type'] + val = string_utils.convert_val(val, type_hint=param_type) property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) obj_groups_str = "World" From 53b61194da5fd672213063a3b5e1701b4d6e45ac Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 6 Jun 2022 10:48:45 -0700 Subject: [PATCH 149/278] Add hider:sampleoffset and hider:samplestride options. --- .../rfb_node_desc_param.py | 2 +- rman_config/config/rman_properties_scene.json | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py index 9e3267b1..b6857074 100644 --- a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py +++ b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py @@ -23,7 +23,7 @@ def blender_finalize(obj): if hasattr(obj, 'help'): obj.help = obj.help.replace('\\"', '"') - obj.help = obj.help.replace("'", "\\'") + #obj.help = obj.help.replace("'", "\\'") obj.help = obj.help.replace('
', '\n') class RfbNodeDescParamXML(NodeDescParamXML): diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index c53bb8b4..ac162399 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -529,7 +529,25 @@ ], "widget": false, "help": "Intended exposure Bracket [min max] in post-production to help inform the adaptive sampling." - }, + }, + { + "panel": "RENDER_PT_renderman_sampling", + "name": "hider_sampleoffset", + "riopt": "hider:sampleoffset", + "label": "Sample Offset", + "type": "int", + "default": 0, + "help": "This allows several images to be rendered in parallel (with different sampleoffset values) and then combined.

With non-adaptive sampling:
Let's say you render four images with 256 samples each, with sampleoffsets 0, 256, 512, and 768. If you combine those four images, you'll get exactly the same image as if you had rendered a single image with 1024 samples.

With adaptive sampling:
Let's say you again render four images, each with 'maxsamples' 256, with sampleoffsets 0, 256, 512, and 768. Let's say that due to adaptive sampling, some given pixel only gets 64 pixel samples in each of the four images. Then the combined image has been rendered with sample numbers 0-63, 256-319, 512-575, and 768-831. Due to the stratification of the samples, this is not quite as good as if you had rendered a single image with 256 consecutive samples. However, it is still better than rendering a single image with only 64 samples." + }, + { + "panel": "RENDER_PT_renderman_sampling", + "name": "hider_samplestride", + "riopt": "hider:samplestride", + "label": "Sample Stride", + "type": "int", + "default": 1, + "help": "This facilitates rendering several images in parallel (with the same samplestride but different sampleoffset) and then combine them incrementally during interactive rendering. Default value is 1.

With non-adaptive sampling:
Let's say you render four images with 256 samples each, with samplestride 4 and sampleoffsets 0, 1, 2, and 3. If you combine those four images, you'll get exactly the same image as if you had rendered a single image with 1024 samples, and during incremental/interactive rendering you'll get the benefit of best use of the stratified progressive sample sequences. If you can 'assemble' images with samples 0-3, 4-7, 8-12, etc during interactive rendering, you've got the benefits of faster rendering without jumping to high samples early on.

With adaptive sampling:
Let's say you again render four images, each with 'maxsamples' 256, with samplestride 4 and sampleoffsets 0, 1, 2, and 3. This time with adaptive sampling (PixelVariance > 0). Since each of the four images is more noisy than the combined image, the adaptive sampling will use more samples before deciding that the image error is acceptable than if it had been rendered as a single image. So the PixelVariance setting may need to be adjusted." + }, { "panel": "RENDER_PT_renderman_sampling", "name": "hider_incremental", From 1812effba610a2bea4531699751a6cde5f6e3a40 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 6 Jun 2022 11:42:28 -0700 Subject: [PATCH 150/278] Modify the RmanSync:update_global_options function to take a prop_name, so we only update that option, instead all of the options. --- rfb_utils/property_callbacks.py | 4 ++-- rfb_utils/property_utils.py | 15 +++++++++++++++ rfb_utils/scenegraph_utils.py | 4 ++-- rman_config/config/rman_properties_scene.json | 8 ++++---- rman_scene.py | 14 +------------- rman_scene_sync.py | 13 +++++++++---- rman_ui/rman_ui_viewport.py | 2 -- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/rfb_utils/property_callbacks.py b/rfb_utils/property_callbacks.py index 238e9175..18d6c0ed 100644 --- a/rfb_utils/property_callbacks.py +++ b/rfb_utils/property_callbacks.py @@ -209,8 +209,8 @@ def update_integrator_func(self, context): update_conditional_visops(node) scenegraph_utils.update_sg_integrator(context) -def update_options_func(self, context): - scenegraph_utils.update_sg_options(context) +def update_options_func(self, s, context): + scenegraph_utils.update_sg_options(s, context) def update_root_node_func(self, context): scenegraph_utils.update_sg_root_node(context) \ No newline at end of file diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index c325e3a1..e08f5394 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -266,6 +266,21 @@ def set_primvar_bl_props(primvars, rm, inherit_node=None): param_type = meta['renderman_type'] set_rix_param(primvars, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) +def set_rioption_bl_prop(options, prop_name, meta, rm): + if 'riopt' not in meta: + return + + val = getattr(rm, prop_name) + ri_name = meta['riopt'] + is_array = False + array_len = -1 + if 'arraySize' in meta: + is_array = True + array_len = meta['arraySize'] + param_type = meta['renderman_type'] + val = string_utils.convert_val(val, type_hint=param_type) + set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + def build_output_param_str(rman_sg_node, mat_name, from_node, from_socket, convert_socket=False, param_type=''): nodes_to_blnodeinfo = getattr(rman_sg_node, 'nodes_to_blnodeinfo', dict()) if from_node in nodes_to_blnodeinfo: diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index 4e0bec6d..a22d8f35 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -18,10 +18,10 @@ def update_sg_integrator(context): rr = rman_render.RmanRender.get_rman_render() rr.rman_scene_sync.update_integrator(context) -def update_sg_options(context): +def update_sg_options(prop_name, context): from .. import rman_render rr = rman_render.RmanRender.get_rman_render() - rr.rman_scene_sync.update_global_options(context) + rr.rman_scene_sync.update_global_options(prop_name, context) def update_sg_root_node(context): from .. import rman_render diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index ac162399..991c3f65 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -172,7 +172,7 @@ "slidermin": 0, "slidermax": 4096, "editable": true, - "update_function_name": "update_options_func", + "update_function_name": "lambda s, c: update_options_func(s, 'ipr_hider_minSamples', c)", "help": "When set to zero this value is automatically computed as the square root of the max samples." }, { @@ -194,7 +194,7 @@ "High Quality": 2048 }, "editable": true, - "update_function_name": "update_options_func", + "update_function_name": "lambda s, c: update_options_func(s, 'ipr_hider_maxSamples', c)", "help": "The maximum number of camera rays to be traced for each pixel. When adaptive sampling is enabled (ie. Pixel Variance is greater than zero), fewer rays may be traced in smoother regions of the image." }, { @@ -215,7 +215,7 @@ "High Quality": 0.01 }, "editable": true, - "update_function_name": "update_options_func", + "update_function_name": "lambda s, c: update_options_func(s, 'ipr_ri_pixelVariance', c)", "help": "This value is applied during IPR. Adaptive sampling is done when Pixel Variance is greater than zero. Reducing this value increases the likelihood that more rays will be traced while increasing its value allows undersampling." }, { @@ -230,7 +230,7 @@ "max": 6, "widget": false, "editable": true, - "update_function_name": "update_options_func", + "update_function_name": "lambda s, c: update_options_func(s, 'hider_decidither', c)", "help": "This value is only applied during IPR. The value determines how much refinement (in a dither pattern) will be applied to the image during interactive rendering. 0 means full refinement up to a value of 6 which is the least refinement per iteration." }, { diff --git a/rman_scene.py b/rman_scene.py index 02b9ea8b..e770cc49 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -1135,19 +1135,7 @@ def export_global_options(self): # set any properties marked riopt in the config file for prop_name, meta in rm.prop_meta.items(): - if 'riopt' not in meta: - continue - - val = getattr(rm, prop_name) - ri_name = meta['riopt'] - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - val = string_utils.convert_val(val, type_hint=param_type) - property_utils.set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + property_utils.set_rioption_bl_prop(options, prop_name, meta, rm) # threads if not self.external_render: diff --git a/rman_scene_sync.py b/rman_scene_sync.py index b98af960..4224d3b3 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -920,14 +920,19 @@ def update_viewport_res_mult(self, context): translator.update_transform(None, rman_sg_camera) self.rman_scene.export_viewport_stats() - def update_global_options(self, context): + def update_global_options(self, prop_name, context): if not self.rman_render.rman_interactive_running: return + from .rfb_utils import property_utils self.rman_scene.bl_scene = context.scene + rm = self.rman_scene.bl_scene.renderman with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.export_global_options() - self.rman_scene.export_hider() - self.rman_scene.export_viewport_stats() + options = self.rman_scene.sg_scene.GetOptions() + meta = rm.prop_meta[prop_name] + property_utils.set_rioption_bl_prop(options, prop_name, meta, rm) + self.rman_scene.sg_scene.SetOptions(options) + self.rman_scene.export_viewport_stats() + def update_root_node_func(self, context): if not self.rman_render.rman_interactive_running: diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index b988f0bf..227cdab3 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -138,10 +138,8 @@ class PRMAN_OT_Viewport_Refinement(bpy.types.Operator): ) def execute(self, context): - rman_render = RmanRender.get_rman_render() rm = context.scene.renderman rm.hider_decidither = int(self.viewport_hider_decidither) - rman_render.rman_scene_sync.update_global_options(context) return {"FINISHED"} From 78a0d987422c8cb326437deaf917ace2595f2436 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 6 Jun 2022 14:36:18 -0700 Subject: [PATCH 151/278] Add a similar editing code path for simple ri attributes, that we did for ri options. --- rfb_utils/generate_property_utils.py | 51 +++++----- rfb_utils/property_callbacks.py | 7 +- rfb_utils/property_utils.py | 93 +++++++++++++------ .../rfb_node_desc_param.py | 1 + rfb_utils/scenegraph_utils.py | 9 +- rman_config/__init__.py | 13 ++- .../config/rman_properties_object.json | 31 ++++++- rman_config/config/rman_properties_scene.json | 4 +- rman_scene.py | 14 +-- rman_scene_sync.py | 52 +++++++++-- rman_translators/rman_translator.py | 34 +------ 11 files changed, 198 insertions(+), 111 deletions(-) diff --git a/rfb_utils/generate_property_utils.py b/rfb_utils/generate_property_utils.py index 20d34329..9f7d5807 100644 --- a/rfb_utils/generate_property_utils.py +++ b/rfb_utils/generate_property_utils.py @@ -139,7 +139,7 @@ def is_array(ndp): setattr(node, param_name, sub_prop_names) return True -def generate_property(node, sp, update_function=None): +def generate_property(node, sp, update_function=None, set_function=None): options = {'ANIMATABLE'} param_name = sp._name renderman_name = param_name @@ -219,6 +219,11 @@ def generate_property(node, sp, update_function=None): exec('update_func = %s' % update_function, globals(), lcls) update_function = lcls['update_func'] + if isinstance(set_function, str): + lcls = locals() + exec('set_func = %s' % set_function, globals(), lcls) + set_function = lcls['set_func'] + if param_widget == 'colorramp': from ..rman_properties.rman_properties_misc import RendermanBlColorRamp @@ -251,13 +256,14 @@ def generate_property(node, sp, update_function=None): default=0.0, precision=3, step=prop_stepsize, description=param_help, + set=set_function, update=update_function) else: if param_widget in ['checkbox', 'switch']: prop = BoolProperty(name=param_label, default=bool(param_default), - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) elif param_widget == 'mapper': items = [] in_items = False @@ -282,7 +288,7 @@ def generate_property(node, sp, update_function=None): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: param_min = sp.min if hasattr(sp, 'min') else (-1.0 * sys.float_info.max) param_max = sp.max if hasattr(sp, 'max') else sys.float_info.max @@ -293,7 +299,7 @@ def generate_property(node, sp, update_function=None): default=param_default, precision=3, soft_min=param_min, soft_max=param_max, step=prop_stepsize, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: param_min = sp.min if hasattr(sp, 'min') else (-1.0 * sys.float_info.max) @@ -305,7 +311,7 @@ def generate_property(node, sp, update_function=None): default=param_default, precision=3, soft_min=param_min, soft_max=param_max, step=prop_stepsize, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'float' @@ -314,7 +320,7 @@ def generate_property(node, sp, update_function=None): if sp.is_array(): prop = IntProperty(name=param_label, default=0, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: param_default = int(param_default) if param_default else 0 # make invertT default 0 @@ -324,7 +330,7 @@ def generate_property(node, sp, update_function=None): if param_widget in ['checkbox', 'switch']: prop = BoolProperty(name=param_label, default=bool(param_default), - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) elif param_widget == 'displaymetadata': from ..rman_bl_nodes.rman_bl_nodes_props import RendermanDspyMetaGroup @@ -365,7 +371,7 @@ def generate_property(node, sp, update_function=None): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: param_min = int(sp.min) if hasattr(sp, 'min') else 0 param_max = int(sp.max) if hasattr(sp, 'max') else 2 ** 31 - 1 @@ -374,7 +380,7 @@ def generate_property(node, sp, update_function=None): default=param_default, soft_min=param_min, soft_max=param_max, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: param_min = int(sp.min) if hasattr(sp, 'min') else 0 @@ -384,7 +390,7 @@ def generate_property(node, sp, update_function=None): default=param_default, soft_min=param_min, soft_max=param_max, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'int' elif param_type == 'color': @@ -393,7 +399,7 @@ def generate_property(node, sp, update_function=None): default=(1.0, 1.0, 1.0), size=3, subtype="COLOR", soft_min=0.0, soft_max=1.0, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: if param_default == 'null' or param_default is None: param_default = (0.0,0.0,0.0) @@ -401,13 +407,13 @@ def generate_property(node, sp, update_function=None): default=param_default, size=3, subtype="COLOR", soft_min=0.0, soft_max=1.0, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'color' elif param_type == 'shader': param_default = '' prop = StringProperty(name=param_label, default=param_default, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'string' elif param_type in ['string', 'struct', 'vstruct', 'bxdf']: if param_default is None: @@ -424,7 +430,7 @@ def generate_property(node, sp, update_function=None): if is_ies: prop = StringProperty(name=param_label, default=param_default, subtype="FILE_PATH", - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: prop = StringProperty(name=param_label, default=param_default, subtype="FILE_PATH", @@ -452,6 +458,7 @@ def colorspace_names_options(self, context): prop = EnumProperty(name=param_label, description=param_help, items=colorspace_names_options, + set=set_function, update=update_function) else: items = [] @@ -480,6 +487,7 @@ def colorspace_names_options(self, context): prop = EnumProperty(name=param_label, default=param_default, description=param_help, items=items, + set=set_function, update=update_function) else: # for strings, assume the first item is the default @@ -488,6 +496,7 @@ def colorspace_names_options(self, context): prop = EnumProperty(name=param_label, default=param_default, description=param_help, items=items, + set=set_function, update=update_function) elif param_widget == 'bl_scenegraphlocation': @@ -499,7 +508,7 @@ def colorspace_names_options(self, context): else: prop = StringProperty(name=param_label, default=str(param_default), - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = param_type elif param_type in ['vector', 'normal']: @@ -508,7 +517,7 @@ def colorspace_names_options(self, context): prop = FloatVectorProperty(name=param_label, default=param_default, size=3, subtype="NONE", - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = param_type elif param_type == 'point': if param_default is None: @@ -516,7 +525,7 @@ def colorspace_names_options(self, context): prop = FloatVectorProperty(name=param_label, default=param_default, size=3, subtype="XYZ", - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = param_type elif param_type == 'int2': param_type = 'int' @@ -551,11 +560,11 @@ def colorspace_names_options(self, context): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: prop = IntVectorProperty(name=param_label, default=param_default, size=2, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'int' prop_meta['arraySize'] = 2 @@ -592,12 +601,12 @@ def colorspace_names_options(self, context): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) else: prop = FloatVectorProperty(name=param_label, default=param_default, size=2, step=prop_stepsize, - description=param_help, update=update_function) + description=param_help, set=set_function, update=update_function) renderman_type = 'float' prop_meta['arraySize'] = 2 diff --git a/rfb_utils/property_callbacks.py b/rfb_utils/property_callbacks.py index 18d6c0ed..c5d40558 100644 --- a/rfb_utils/property_callbacks.py +++ b/rfb_utils/property_callbacks.py @@ -212,5 +212,8 @@ def update_integrator_func(self, context): def update_options_func(self, s, context): scenegraph_utils.update_sg_options(s, context) -def update_root_node_func(self, context): - scenegraph_utils.update_sg_root_node(context) \ No newline at end of file +def update_root_node_func(self, s, context): + scenegraph_utils.update_sg_root_node(s, context) + +def update_riattr_func(self, s, context): + scenegraph_utils.update_sg_node_riattr(s, context) \ No newline at end of file diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index e08f5394..a33eb746 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -236,35 +236,38 @@ def set_rix_param(params, param_type, param_name, val, is_reference=False, is_ar def set_primvar_bl_props(primvars, rm, inherit_node=None): # set any properties marked primvar in the config file for prop_name, meta in rm.prop_meta.items(): - if 'primvar' not in meta: - continue - - conditionalVisOps = meta.get('conditionalVisOps', None) - if conditionalVisOps: - # check conditionalVisOps to see if this primvar applies - # to this object - expr = conditionalVisOps.get('expr', None) - node = rm - if expr and not eval(expr): - continue - - val = getattr(rm, prop_name) - if not val: - continue + set_primvar_bl_prop(primvars, prop_name, meta, rm, inherit_node=inherit_node) - if 'inheritable' in meta: - if float(val) == meta['inherit_true_value']: - if inherit_node and hasattr(inherit_node, prop_name): - val = getattr(inherit_node, prop_name) +def set_primvar_bl_prop(primvars, prop_name, meta, rm, inherit_node): + if 'primvar' not in meta: + return + + conditionalVisOps = meta.get('conditionalVisOps', None) + if conditionalVisOps: + # check conditionalVisOps to see if this primvar applies + # to this object + expr = conditionalVisOps.get('expr', None) + node = rm + if expr and not eval(expr): + return - ri_name = meta['primvar'] - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - set_rix_param(primvars, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + val = getattr(rm, prop_name) + if not val: + return + + if 'inheritable' in meta: + if float(val) == meta['inherit_true_value']: + if inherit_node and hasattr(inherit_node, prop_name): + val = getattr(inherit_node, prop_name) + + ri_name = meta['primvar'] + is_array = False + array_len = -1 + if 'arraySize' in meta: + is_array = True + array_len = meta['arraySize'] + param_type = meta['renderman_type'] + set_rix_param(primvars, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) def set_rioption_bl_prop(options, prop_name, meta, rm): if 'riopt' not in meta: @@ -281,6 +284,42 @@ def set_rioption_bl_prop(options, prop_name, meta, rm): val = string_utils.convert_val(val, type_hint=param_type) set_rix_param(options, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) +def set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=True, remove=True): + if 'riattr' not in meta: + return + + conditionalVisOps = meta.get('conditionalVisOps', None) + if conditionalVisOps: + # check conditionalVisOps to see if this riattr applies + # to this object + expr = conditionalVisOps.get('expr', None) + if expr and not eval(expr): + return + + val = getattr(rm, prop_name) + ri_name = meta['riattr'] + if check_inherit and 'inheritable' in meta: + cond = meta['inherit_true_value'] + if isinstance(cond, str): + if exec(cond): + if remove: + attrs.Remove(ri_name) + return + elif float(val) == cond: + if remove: + attrs.Remove(ri_name) + return + + is_array = False + array_len = -1 + if 'arraySize' in meta: + is_array = True + array_len = meta['arraySize'] + param_type = meta['renderman_type'] + val = string_utils.convert_val(val, type_hint=param_type) + set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + + def build_output_param_str(rman_sg_node, mat_name, from_node, from_socket, convert_socket=False, param_type=''): nodes_to_blnodeinfo = getattr(rman_sg_node, 'nodes_to_blnodeinfo', dict()) if from_node in nodes_to_blnodeinfo: diff --git a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py index b6857074..98c1797f 100644 --- a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py +++ b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py @@ -11,6 +11,7 @@ NodeDescParam.optional_attrs = NodeDescParam.optional_attrs + [] NodeDescParamJSON.keywords = NodeDescParamJSON.keywords + ['panel', 'inheritable', 'inherit_true_value', 'update_function_name', 'update_function', + 'set_function_name', 'set_function', 'readOnly', 'always_write'] def blender_finalize(obj): diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index a22d8f35..24cd9e2e 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -23,10 +23,15 @@ def update_sg_options(prop_name, context): rr = rman_render.RmanRender.get_rman_render() rr.rman_scene_sync.update_global_options(prop_name, context) -def update_sg_root_node(context): +def update_sg_root_node(prop_name, context): from .. import rman_render rr = rman_render.RmanRender.get_rman_render() - rr.rman_scene_sync.update_root_node_func(context) + rr.rman_scene_sync.update_root_node_func(prop_name, context) + +def update_sg_node_riattr(prop_name, context): + from .. import rman_render + rr = rman_render.RmanRender.get_rman_render() + rr.rman_scene_sync.update_sg_node_riattr(prop_name, context) def export_vol_aggregate(bl_scene, primvar, ob): vol_aggregate_group = [] diff --git a/rman_config/__init__.py b/rman_config/__init__.py index 8d6223a2..2f14189b 100644 --- a/rman_config/__init__.py +++ b/rman_config/__init__.py @@ -73,6 +73,7 @@ def addpage(ndp): for param_name, ndp in config.params.items(): update_func = None + set_func = None if hasattr(ndp, 'update_function'): # this code tries to dynamically add a function to cls # don't ask me why this works. @@ -83,11 +84,21 @@ def addpage(ndp): setattr(cls, ndp.update_function_name, update_func) elif hasattr(ndp, 'update_function_name'): update_func = ndp.update_function_name + + if hasattr(ndp, 'set_function'): + lcls = locals() + exec(ndp.set_function, globals(), lcls) + exec('set_func = %s' % ndp.set_function_name, globals(), lcls) + set_func = lcls['set_func'] + setattr(cls, ndp.set_function_name, set_func) + elif hasattr(ndp, 'set_function_name'): + set_func = ndp.set_function_name + if ndp.is_array(): if generate_property_utils.generate_array_property(cls, prop_names, prop_meta, ndp): addpage(ndp) continue - name, meta, prop = generate_property_utils.generate_property(cls, ndp, update_function=update_func) + name, meta, prop = generate_property_utils.generate_property(cls, ndp, update_function=update_func, set_function=set_func) if prop: cls.__annotations__[ndp._name] = prop prop_names.append(ndp.name) diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index ca7e5f76..b55c7fc6 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -398,6 +398,7 @@ "editable": true, "inheritable": true, "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_matteObject', c)", "page": "Shading", "help": "This object is rendered as a black hole" }, @@ -413,6 +414,7 @@ "editable": true, "inheritable": true, "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_holdout', c)", "page": "Shading", "help": "Useful in holdout workflow. These objects collect reflections, shadows, and transmission." }, @@ -426,6 +428,7 @@ "editable": true, "inheritable": true, "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_indexofrefraction', c)", "page": "Shading", "help": "Override material IOR for nested dielectrics." }, @@ -437,6 +440,7 @@ "type": "int", "editable": true, "default": 1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_minsamples', c)", "page": "Shading", "help": "Min Pixel Samples. For fine-tuning adaptive sampling." }, @@ -448,6 +452,7 @@ "type": "float", "editable": true, "default": 1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_relativepixelvariance', c)", "page": "Shading", "help": "PixelVariance multiplier for camera visible objects. For fine-tuning adaptive sampling." }, @@ -461,6 +466,7 @@ "inheritable": true, "inherit_true_value": -1, "editable": true, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_maxDiffuseDepth', c)", "page": "Trace", "help": "Maximum diffuse light bounces." }, @@ -474,6 +480,7 @@ "default": -1, "inheritable": true, "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_maxSpecularDepth', c)", "page": "Trace", "help": "Maximum specular light bounces" }, @@ -487,6 +494,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_reflectExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Exclude object groups from reflections" }, @@ -500,6 +508,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_reflectSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Object groups visible to reflections" }, @@ -513,6 +522,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_shadowExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Exclude object groups from casting shadows" }, @@ -526,6 +536,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_shadowSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Object groups active in shadows" }, @@ -539,6 +550,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_transmitExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Exclude object groups from transmission/refractions" }, @@ -552,6 +564,7 @@ "page": "Trace", "editable": true, "widget": "propSearch", + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_transmitSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Object groups visible to transmission/refractions" }, @@ -565,6 +578,7 @@ "default": 0, "min": 0, "max": 31, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_intersectPriority', c)", "page": "Nested Dielectrics", "help": "Raytrace intersection priority for nested dielectrics" }, @@ -914,7 +928,8 @@ "editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, - "inherit_true_value": -1, + "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_visibilityCamera', c)", "page": "Visibility", "help": "Indicates if object is visible to camera rays" }, @@ -929,7 +944,8 @@ "editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, - "inherit_true_value": -1, + "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_visibilityIndirect', c)", "page": "Visibility", "help": "Indicates if object is visible to indirect (reflection and specular refraction) rays" }, @@ -944,7 +960,8 @@ "editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, - "inherit_true_value": -1, + "inherit_true_value": -1, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_visibilityTransmission', c)", "page": "Visibility", "help": "Indicates if object is visible to shadow rays" }, @@ -960,6 +977,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID0', c)", "help": "Matte ID 0 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -974,6 +992,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID1', c)", "help": "Matte ID 1 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -988,6 +1007,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID2', c)", "help": "Matte ID 2 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -1002,6 +1022,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID3', c)", "help": "Matte ID 2 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -1016,6 +1037,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID4', c)", "help": "Matte ID 4 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -1030,6 +1052,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID5', c)", "help": "Matte ID 5 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -1044,6 +1067,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID6', c)", "help": "Matte ID 6 Color, you also need to add the PxrMatteID node to your bxdf" }, { @@ -1058,6 +1082,7 @@ 0, 0 ], + "update_function_name": "lambda s, c: update_riattr_func(s, 'user_MatteID7', c)", "help": "Matte ID 7 Color, you also need to add the PxrMatteID node to your bxdf" }, { diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 991c3f65..cb01b779 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -566,7 +566,7 @@ "label": "Max Specular Depth", "type": "int", "editable": true, - "update_function_name": "update_root_node_func", + "update_function_name": "lambda s, c: update_root_node_func(s, 'ri_maxSpecularDepth', c)", "default": 4, "min": 0, "slidermax": 15 @@ -579,7 +579,7 @@ "label": "Max Diffuse Depth", "type": "int", "editable": true, - "update_function_name": "update_root_node_func", + "update_function_name": "lambda s, c: update_root_node_func(s, 'ri_maxDiffuseDepth', c)", "default": 1, "min": 0, "slidermax": 15 diff --git a/rman_scene.py b/rman_scene.py index e770cc49..e4252b66 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -510,19 +510,7 @@ def export_root_sg_node(self): # set any properties marked riattr in the config file for prop_name, meta in rm.prop_meta.items(): - if 'riattr' not in meta: - continue - - val = getattr(rm, prop_name) - ri_name = meta['riattr'] - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - val = string_utils.convert_val(val, type_hint=param_type) - property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False, remove=False) if rm.invert_light_linking: all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)] diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 4224d3b3..d48f2ed3 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -22,6 +22,9 @@ class RmanUpdate: considered geometry updates is_updated_transform (bool) - Whether the geometry was transformed is_updated_shading (bool) - If the shader on the object has changed + is_updated_attributes (bool) - Wether an Ri attribute was changed + updated_prop_name (str) - The name of the Blender property that was changed, for the + is_updated_attributes case do_clear_instances (bool) - Whether we should clear/delete all instances of the prototype ''' @@ -29,6 +32,8 @@ def __init__(self): self.is_updated_geometry = False self.is_updated_transform = False self.is_updated_shading = False + self.is_updated_attributes = False + self.update_prop_name = '' self.do_clear_instances = True class RmanSceneSync(object): @@ -563,7 +568,7 @@ def batch_update_scene(self, context, depsgraph): @time_this def update_scene(self, context, depsgraph): - self.rman_updates = dict() + #self.rman_updates = dict() self.num_instances_changed = False self.frame_number_changed = False self.check_all_instances = False @@ -674,7 +679,8 @@ def update_scene(self, context, depsgraph): self.check_instances() # call txmake all in case of new textures - texture_utils.get_txmanager().txmake_all(blocking=False) + texture_utils.get_txmanager().txmake_all(blocking=False) + self.rman_updates = dict() rfb_log().debug("------End update scene----------") @time_this @@ -804,6 +810,17 @@ def check_instances(self, batch_mode=False): # simply grab the existing instance and update the transform and/or material rman_sg_group = self.rman_scene.get_rman_sg_instance(instance, rman_sg_node, instance_parent, psys, create=False) if rman_sg_group: + if rman_update.is_updated_attributes: + from .rfb_utils import property_utils + attrs = rman_sg_group.sg_node.GetAttributes() + rm = ob_eval.renderman + if is_empty_instancer: + rm = instance_parent.renderman + meta = rm.prop_meta[rman_update.update_prop_name] + + property_utils.set_riattr_bl_prop(attrs, rman_update.update_prop_name, meta, rm, check_inherit=True) + rman_sg_group.sg_node.SetAttributes(attrs) + if rman_update.is_updated_transform: rman_group_translator = self.rman_scene.rman_translators['GROUP'] rman_group_translator.update_transform(instance, rman_sg_group) @@ -929,18 +946,39 @@ def update_global_options(self, prop_name, context): with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): options = self.rman_scene.sg_scene.GetOptions() meta = rm.prop_meta[prop_name] + rfb_log().debug("Update RiOption: %s" % prop_name) property_utils.set_rioption_bl_prop(options, prop_name, meta, rm) self.rman_scene.sg_scene.SetOptions(options) self.rman_scene.export_viewport_stats() - def update_root_node_func(self, context): + def update_root_node_func(self, prop_name, context): if not self.rman_render.rman_interactive_running: - return + return + from .rfb_utils import property_utils self.rman_scene.bl_scene = context.scene - with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): - self.rman_scene.export_root_sg_node() - + rm = self.rman_scene.bl_scene.renderman + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + root_sg = self.rman_scene.get_root_sg_node() + attrs = root_sg.GetAttributes() + meta = rm.prop_meta[prop_name] + rfb_log().debug("Update root node attribute: %s" % prop_name) + property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False) + root_sg.SetAttributes(attrs) + + def update_sg_node_riattr(self, prop_name, context): + if not self.rman_render.rman_interactive_running: + return + from .rfb_utils import property_utils + self.rman_scene.bl_scene = context.scene + ob = context.object + rman_update = RmanUpdate() + rman_update.do_clear_instances = False + rman_update.is_updated_attributes = True + rman_update.update_prop_name = prop_name + self.rman_updates[ob.original] = rman_update + rfb_log().debug("Updated RiAttribute: %s" % rman_update.update_prop_name) + def update_material(self, mat): if not self.rman_render.rman_interactive_running: return diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 2d58879a..c3b76274 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -192,39 +192,7 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): # set any properties marked riattr in the config file for prop_name, meta in rm.prop_meta.items(): - if 'riattr' not in meta: - continue - - conditionalVisOps = meta.get('conditionalVisOps', None) - if conditionalVisOps: - # check conditionalVisOps to see if this riattr applies - # to this object - expr = conditionalVisOps.get('expr', None) - if expr and not eval(expr): - continue - - ri_name = meta['riattr'] - val = getattr(rm, prop_name) - if 'inheritable' in meta: - cond = meta['inherit_true_value'] - if isinstance(cond, str): - if exec(cond): - if remove: - attrs.Remove(ri_name) - continue - elif float(val) == cond: - if remove: - attrs.Remove(ri_name) - continue - - is_array = False - array_len = -1 - if 'arraySize' in meta: - is_array = True - array_len = meta['arraySize'] - param_type = meta['renderman_type'] - val = string_utils.convert_val(val, type_hint=param_type) - property_utils.set_rix_param(attrs, param_type, ri_name, val, is_reference=False, is_array=is_array, array_len=array_len, node=rm, prop_name=prop_name) + property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=True, remove=remove) obj_groups_str = "World" obj_groups_str += "," + name From 965bf1e0ce71c9d7831568008f914f8b2576f5af Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 6 Jun 2022 16:09:16 -0700 Subject: [PATCH 152/278] Add a similar code path for simple primvar updates, like micropolygonlength. --- rfb_utils/property_callbacks.py | 5 ++- rfb_utils/scenegraph_utils.py | 7 +++- rman_config/config/rman_properties_mesh.json | 3 ++ .../config/rman_properties_object.json | 16 ++++++++- .../config/rman_properties_volume.json | 3 +- rman_scene_sync.py | 35 ++++++++++++++----- rman_translators/rman_mesh_translator.py | 13 +++++++ rman_translators/rman_openvdb_translator.py | 12 +++++++ rman_translators/rman_translator.py | 18 ++++++++++ rman_translators/rman_volume_translator.py | 6 ++++ 10 files changed, 105 insertions(+), 13 deletions(-) diff --git a/rfb_utils/property_callbacks.py b/rfb_utils/property_callbacks.py index c5d40558..a980e3c6 100644 --- a/rfb_utils/property_callbacks.py +++ b/rfb_utils/property_callbacks.py @@ -216,4 +216,7 @@ def update_root_node_func(self, s, context): scenegraph_utils.update_sg_root_node(s, context) def update_riattr_func(self, s, context): - scenegraph_utils.update_sg_node_riattr(s, context) \ No newline at end of file + scenegraph_utils.update_sg_node_riattr(s, context) + +def update_primvar_func(self, s, context): + scenegraph_utils.update_sg_node_primvar(s, context) \ No newline at end of file diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index 24cd9e2e..0972f2fd 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -31,7 +31,12 @@ def update_sg_root_node(prop_name, context): def update_sg_node_riattr(prop_name, context): from .. import rman_render rr = rman_render.RmanRender.get_rman_render() - rr.rman_scene_sync.update_sg_node_riattr(prop_name, context) + rr.rman_scene_sync.update_sg_node_riattr(prop_name, context) + +def update_sg_node_primvar(prop_name, context): + from .. import rman_render + rr = rman_render.RmanRender.get_rman_render() + rr.rman_scene_sync.update_sg_node_primvar(prop_name, context) def export_vol_aggregate(bl_scene, primvar, ob): vol_aggregate_group = [] diff --git a/rman_config/config/rman_properties_mesh.json b/rman_config/config/rman_properties_mesh.json index 2ce865f5..1c28ba0b 100644 --- a/rman_config/config/rman_properties_mesh.json +++ b/rman_config/config/rman_properties_mesh.json @@ -11,6 +11,7 @@ "default": 0, "widget": "checkBox", "page": "Polygons", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_smoothnormals', c)", "help": "Render polygons with smooth normals." }, { @@ -23,6 +24,7 @@ "default": 0, "widget": "checkBox", "page": "Polygons", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_preventPolyCracks', c)", "help": "Prevent polygon cracks on sharp edges, as long as both sides of the edge have similar displacement values, at the cost of slightly warping your displacement." }, { @@ -47,6 +49,7 @@ "widget": "mapper", "options": "Yes:1|No:0", "page": "Subdivision Mesh", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_watertight', c)", "help": "Fix pin-holes in Subdivision Surfaces.
It may slow down dicing and time to first pixel.", "conditionalVisOps": { "conditionalVisOp": "notEqualTo", diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index b55c7fc6..d879a4cc 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -665,6 +665,7 @@ "type": "float", "editable": true, "default": 0.1, + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_displacementBound', c)", "page": "Displacement" }, { @@ -678,6 +679,7 @@ "widget": "popup", "options": "world|object", "page": "Displacement", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_displacementCoordSys', c)", "help": "Coordinate system of the displacement bound. For example, a scaled up ground plane may want the displacement bound in world space, while a character would typically specify the value in object space." }, { @@ -692,6 +694,7 @@ "inherit_true_value": -1.0, "page": "Dicing", "help": "Micropolygon distance in raster space for 'instanceprojection' dicing. Values are expressed in pixel size.", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_micropolygonlength', c)", "conditionalVisOps": { "conditionalVis1Path": "bl_object_type", "conditionalVis2Op": "notEqualTo", @@ -714,6 +717,7 @@ "editable": true, "always_write": true, "page": "Dicing", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_micropolygonlength_volume', c)", "help": "Micropolygon distance in raster space for 'instanceprojection' dicing. Values are expressed in pixel size.", "conditionalVisOps": { "conditionalVis1Path": "bl_object_type", @@ -737,6 +741,7 @@ "label": "Dicing Strategy", "default": "instanceprojection", "widget": "popup", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_diceStrategy', c)", "options": "instanceprojection|worlddistance|objectdistance", "help": "Dicing method of objects within the viewing frustum.
instanceprojection: Use object size in screen space.
worlddistance: Use object size in world space
objectdistance: Use object size in object space." }, @@ -747,7 +752,8 @@ "primvar": "dice:minlength", "label": "Min Length", "type": "float", - "editable": true, + "editable": true, + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_minlength', c)", "default": 1 }, { @@ -760,6 +766,7 @@ "default": "world", "widget": "popup", "editable": true, + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_minlengthspace', c)", "options": "world|object|camera" }, @@ -773,6 +780,7 @@ "default": 1, "widget": "checkbox", "page": "Dicing", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_rasterorient', c)", "help": "Changes micropolygon size based on the viewing angle of an object.
When rasterorient is on, surfaces are diced more coarsely at a glancing angle.
It may be useful to turn off for instances as their geometry is seen from different angles
or for objects where displacement details are lost.", "conditionalVisOps": { "conditionalVisOp": "equalTo", @@ -785,6 +793,7 @@ "page": "Dicing", "name": "rman_diceDistanceLength", "primvar": "dice:worlddistancelength", + "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_diceDistanceLength', c)", "type": "float", "editable": true, "label": "Dicing Distance Length", @@ -820,6 +829,7 @@ "editable": true, "widget": "bl_scenegraphLocation", "options": "nodeType:bpy.types.Camera", + "update_function_name": "lambda s, c: update_primvar_func(s, 'dice_referenceCamera', c)", "default": "", "conditionalVisOps": { "conditionalVisOp": "equalTo", @@ -849,6 +859,7 @@ "conditionalVis2Path": "bl_object_type", "conditionalVis2Value": "RI_VOLUME" }, + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_temporalmethod', c)", "help": "Method of generating temporal data for volume rendering." }, { @@ -871,6 +882,7 @@ "conditionalVis2Path": "bl_object_type", "conditionalVis2Value": "RI_VOLUME" }, + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_fps', c)", "help": "The frames per second for volumetric velocity data. The velocity data will be divided by this quantity to derive the velocity for the frame." }, { @@ -893,6 +905,7 @@ "conditionalVis2Path": "bl_object_type", "conditionalVis2Value": "RI_VOLUME" }, + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_shutteroffset', c)", "help": "The shutter offset used to interpret volumetric velocity. A value of 1 will use the current position of the object and the position of the object on the next frame as the time interval to use for motion blur. A value of -1 will use the position of the object on the previous frame and the current position of the object as the time. A value of 0 will generate an interval which begins halfway through the previous frame and ends halfway into the next frame." }, { @@ -915,6 +928,7 @@ "conditionalVis2Path": "bl_object_type", "conditionalVis2Value": "RI_VOLUME" }, + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_velocityshuttercorrection', c)", "help": "The shutter offset used to interpret volumetric velocity. A value of 1 will use the current position of the object and the position of the object on the next frame as the time interval to use for motion blur. A value of -1 will use the position of the object on the previous frame and the current position of the object as the time. A value of 0 will generate an interval which begins halfway through the previous frame and ends halfway into the next frame." }, { diff --git a/rman_config/config/rman_properties_volume.json b/rman_config/config/rman_properties_volume.json index baf15137..955c3bce 100644 --- a/rman_config/config/rman_properties_volume.json +++ b/rman_config/config/rman_properties_volume.json @@ -41,7 +41,8 @@ "widget": "checkbox", "primvar": "volume:dsominmax", "page": "", - "help": "Currently only used for aggregate volumes, and only for volumes that use an ImplicitField DSO. If set to 1, the DSO may be able to use stored information from the file directly to compute the minimum and maximum values within the volume. This may allow the renderer to avoid shading the volume up front, greatly decreasing time to first pixel. This can only be enabled if the density signal from the volume is used directly, or if the density signal is modulated only by the DSO itself. Any shading modifications of the density signal requires setting this parameter to off." + "help": "Currently only used for aggregate volumes, and only for volumes that use an ImplicitField DSO. If set to 1, the DSO may be able to use stored information from the file directly to compute the minimum and maximum values within the volume. This may allow the renderer to avoid shading the volume up front, greatly decreasing time to first pixel. This can only be enabled if the density signal from the volume is used directly, or if the density signal is modulated only by the DSO itself. Any shading modifications of the density signal requires setting this parameter to off.", + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_dsominmax', c)" } ] } \ No newline at end of file diff --git a/rman_scene_sync.py b/rman_scene_sync.py index d48f2ed3..bf127033 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -23,8 +23,8 @@ class RmanUpdate: is_updated_transform (bool) - Whether the geometry was transformed is_updated_shading (bool) - If the shader on the object has changed is_updated_attributes (bool) - Wether an Ri attribute was changed - updated_prop_name (str) - The name of the Blender property that was changed, for the - is_updated_attributes case + updated_prop_name (str) - The name of the Blender property that was changed, for either + is_updated_attributes or is_updated_geometry case do_clear_instances (bool) - Whether we should clear/delete all instances of the prototype ''' @@ -33,7 +33,7 @@ def __init__(self): self.is_updated_transform = False self.is_updated_shading = False self.is_updated_attributes = False - self.update_prop_name = '' + self.updated_prop_name = None self.do_clear_instances = True class RmanSceneSync(object): @@ -774,10 +774,14 @@ def check_instances(self, batch_mode=False): if rman_sg_node and not is_new_object and not instance.is_instance: if rman_update.is_updated_geometry and proto_key not in already_udpated: translator = self.rman_scene.rman_translators.get(rman_type, None) - rfb_log().debug("\tUpdating Object: %s" % proto_key) - translator.update(ob_eval, rman_sg_node) - rman_sg_node.shared_attrs.Clear() - self.update_particle_emitters(ob_eval) + if rman_update.updated_prop_name: + rfb_log().debug("\tUpdating Single Primvar: %s" % proto_key) + translator.update_primvar(ob_eval, rman_sg_node, rman_update.updated_prop_name) + else: + rfb_log().debug("\tUpdating Object: %s" % proto_key) + translator.update(ob_eval, rman_sg_node) + rman_sg_node.shared_attrs.Clear() + self.update_particle_emitters(ob_eval) already_udpated.append(proto_key) if rman_type in object_utils._RMAN_NO_INSTANCES_: @@ -975,9 +979,22 @@ def update_sg_node_riattr(self, prop_name, context): rman_update = RmanUpdate() rman_update.do_clear_instances = False rman_update.is_updated_attributes = True - rman_update.update_prop_name = prop_name + rman_update.updated_prop_name = prop_name self.rman_updates[ob.original] = rman_update - rfb_log().debug("Updated RiAttribute: %s" % rman_update.update_prop_name) + rfb_log().debug("Updated RiAttribute: %s" % rman_update.updated_prop_name) + + def update_sg_node_primvar(self, prop_name, context): + if not self.rman_render.rman_interactive_running: + return + from .rfb_utils import property_utils + self.rman_scene.bl_scene = context.scene + ob = context.object + rman_update = RmanUpdate() + rman_update.do_clear_instances = False + rman_update.is_updated_geometry = True + rman_update.updated_prop_name = prop_name + self.rman_updates[ob.original] = rman_update + rfb_log().debug("Updated primvar: %s" % rman_update.updated_prop_name) def update_material(self, mat): if not self.rman_render.rman_interactive_running: diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index 019bb2db..ac1a1629 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -352,6 +352,19 @@ def export_deform_sample(self, rman_sg_mesh, ob, time_sample, sg_node=None): ob.to_mesh_clear() + def update_primvar(self, ob, rman_sg_mesh, prop_name): + mesh = ob.to_mesh() + primvars = rman_sg_mesh.sg_node.GetPrimVars() + if prop_name in mesh.renderman.prop_meta: + rm = mesh.renderman + meta = rm.prop_meta[prop_name] + rm_scene = self.rman_scene.bl_scene.renderman + property_utils.set_primvar_bl_prop(primvars, prop_name, meta, rm, inherit_node=rm_scene) + else: + super().update_object_primvar(ob, primvars, prop_name) + rman_sg_mesh.sg_node.SetPrimVars(primvars) + ob.to_mesh_clear() + def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): rm = ob.renderman mesh = input_mesh diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index abc2a61d..f1f8fd4e 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -24,6 +24,18 @@ def export(self, ob, db_name): def export_deform_sample(self, rman_sg_openvdb, ob, time_sample): pass + def update_primvar(self, ob, rman_sg_openvdb, prop_name): + db = ob.data + primvars = rman_sg_openvdb.sg_node.GetPrimVars() + if prop_name in db.renderman.prop_meta: + rm = db.renderman + meta = rm.prop_meta[prop_name] + rm_scene = self.rman_scene.bl_scene.renderman + property_utils.set_primvar_bl_prop(primvars, prop_name, meta, rm, inherit_node=rm_scene) + else: + super().update_object_primvar(ob, primvars, prop_name) + rman_sg_openvdb.sg_node.SetPrimVars(primvars) + def update(self, ob, rman_sg_openvdb): db = ob.data rm = db.renderman diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index c3b76274..f625d66a 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -84,6 +84,24 @@ def export_object_primvars(self, ob, primvars): rm_scene = self.rman_scene.bl_scene.renderman property_utils.set_primvar_bl_props(primvars, rm, inherit_node=rm_scene) + def update_primvar(self, ob, rman_sg_node, prop_name): + pass + + def update_object_primvar(self, ob, primvars, prop_name): + ''' + This method should be called by subclasses of RmanTranslator + in their update_primvar() methods. + + Args: + ob (bpy.types.Object) - Blender Object + primvars (RtPrimVars) - primitive variables + prop_name (str) - name of the Blender property that was updated + ''' + rm = ob.renderman + rm_scene = self.rman_scene.bl_scene.renderman + meta = rm.prop_meta[prop_name] + property_utils.set_primvar_bl_prop(primvars, prop_name, meta, rm, inherit_node=rm_scene) + def export_instance_attributes(self, ob, rman_sg_node, ob_inst): ''' Export attributes that should vary between each instance diff --git a/rman_translators/rman_volume_translator.py b/rman_translators/rman_volume_translator.py index ce6d5e66..dc374f88 100644 --- a/rman_translators/rman_volume_translator.py +++ b/rman_translators/rman_volume_translator.py @@ -2,6 +2,7 @@ from ..rman_sg_nodes.rman_sg_volume import RmanSgVolume from ..rfb_utils import scenegraph_utils from ..rfb_utils import transform_utils +from ..rfb_utils import property_utils from ..rfb_logger import rfb_log import bpy @@ -22,6 +23,11 @@ def export(self, ob, db_name): def export_deform_sample(self, rman_sg_volume, ob, time_sample): pass + def update_primvar(self, ob, rman_sg_volume, prop_name): + primvars = rman_sg_volume.sg_node.GetPrimVars() + super().update_object_primvar(ob, primvars, prop_name) + rman_sg_volume.sg_node.SetPrimVars(primvars) + def update(self, ob, rman_sg_volume): rman_sg_volume.sg_node.Define(0,0,0) primvar = rman_sg_volume.sg_node.GetPrimVars() From 66b9f50f100cc6de92afecd7a4e16999d9c4a558 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 7 Jun 2022 08:25:44 -0700 Subject: [PATCH 153/278] * In the material translator, try using the old name of the sockets, if the new name fails. * Fix issue with convert_val when using type_hint="color" --- rfb_utils/string_utils.py | 3 +-- rman_translators/rman_material_translator.py | 24 ++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 76c9ef3d..36d7d801 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -244,10 +244,9 @@ def convert_val(v, type_hint=None): if type_hint == 'color': converted_val = list(v)[:3] - if type(v) in (mathutils.Vector, mathutils.Color) or\ + elif type(v) in (mathutils.Vector, mathutils.Color) or\ v.__class__.__name__ == 'bpy_prop_array'\ or v.__class__.__name__ == 'Euler': - # BBM modified from if to elif converted_val = list(v) elif type(v) == str and v.startswith('['): diff --git a/rman_translators/rman_material_translator.py b/rman_translators/rman_material_translator.py index e7cc8401..73b52ab6 100644 --- a/rman_translators/rman_material_translator.py +++ b/rman_translators/rman_material_translator.py @@ -114,8 +114,11 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): return True # bxdf - socket = out.inputs['bxdf_in'] - if socket.is_linked and len(socket.links) > 0: + socket = out.inputs.get('bxdf_in', None) + if socket is None: + # try old name + socket = out.inputs.get('Bxdf', None) + if socket and socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='bxdf') if linked_node: bxdfList = [] @@ -138,8 +141,11 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): self.create_pxrdiffuse_node(rman_sg_material, handle) # light - socket = out.inputs['light_in'] - if socket.is_linked and len(socket.links) > 0: + socket = out.inputs.get('light_in', None) + if socket is None: + # try old name + socket = out.inputs.get('Light', None) + if socket and socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='light') if linked_node: lightNodesList = [] @@ -157,8 +163,11 @@ def export_shader_nodetree(self, material, rman_sg_material, handle): rman_sg_material.sg_node.SetLight(lightNodesList) # displacement - socket = out.inputs['displace_in'] - if socket.is_linked and len(socket.links) > 0: + socket = out.inputs.get('displace_in', None) + if socket is None: + # use old name + socket = out.inputs.get('Displacement', None) + if socket and socket.is_linked and len(socket.links) > 0: linked_node = get_root_node(socket.links[0].from_node, type='displace') if linked_node: dispList = [] @@ -261,7 +270,8 @@ def export_simple_shader(self, mat, rman_sg_material, mat_handle=''): bxdf_name = '%s_PxrDisneyBsdf' % name sg_node = self.rman_scene.rman.SGManager.RixSGShader("Bxdf", "PxrDisneyBsdf", bxdf_name) rix_params = sg_node.params - rix_params.SetColor('baseColor', string_utils.convert_val(mat.diffuse_color, 'color')) + diffuse_color = string_utils.convert_val(mat.diffuse_color, type_hint='color') + rix_params.SetColor('baseColor', diffuse_color) rix_params.SetFloat('metallic', mat.metallic ) rix_params.SetFloat('roughness', mat.roughness) rix_params.SetFloat('specReflectScale', mat.metallic ) From 244c3c89357b03039d491132cf64ddacfaa148ce Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 7 Jun 2022 16:17:20 -0700 Subject: [PATCH 154/278] Use name_full for the prototype_key, The name_full contains the library name, when linking to objects in other blend files. --- rfb_utils/object_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index 40b4ccf7..aef5f993 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -160,15 +160,15 @@ def prototype_key(ob): if isinstance(ob, bpy.types.DepsgraphObjectInstance): if ob.is_instance: if ob.object.data: - return '%s-DATA' % ob.object.data.name + return '%s-DATA' % ob.object.data.name_full else: - return '%s-OBJECT' % ob.object.name + return '%s-OBJECT' % ob.object.name_full if ob.object.data: - return '%s-DATA' % ob.object.data.name - return '%s-OBJECT' % ob.object.original.name + return '%s-DATA' % ob.object.data.name_full + return '%s-OBJECT' % ob.object.original.name_full elif ob.data: - return '%s-DATA' % ob.original.data.original.name - return '%s-OBJECT' % ob.original.name + return '%s-DATA' % ob.original.data.original.name_full + return '%s-OBJECT' % ob.original.name_full def curve_is_mesh(ob): ''' From 24b0f064299c7f796d79bb9292d15aed1a76b5e3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Jun 2022 10:35:53 -0700 Subject: [PATCH 155/278] Remove the code that substitutes spaces for '_', in string_expand. Need to think about this some more. --- rfb_utils/string_expr.py | 1 - rfb_utils/string_utils.py | 1 - 2 files changed, 2 deletions(-) diff --git a/rfb_utils/string_expr.py b/rfb_utils/string_expr.py index e36f20d3..c2e39d5e 100644 --- a/rfb_utils/string_expr.py +++ b/rfb_utils/string_expr.py @@ -226,7 +226,6 @@ def expand(self, expr, objTokens={}, asFilePath=False): # get the real path result = filepath_utils.get_real_path(result) - result = result.replace(' ', '_') dirname = os.path.dirname(result) if not os.path.exists(dirname): diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 36d7d801..48e144ed 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -129,7 +129,6 @@ def _resetStringConverter(): # get the real path if string and asFilePath and os.path.isabs(string): string = filepath_utils.get_real_path(string) - string = string.replace(' ', '_') dirname = os.path.dirname(string) if not os.path.exists(dirname): try: From da4e3a6da46bd56feab54210db50bc0c912a89d2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Jun 2022 15:07:40 -0700 Subject: [PATCH 156/278] Add a frame change handler to handle updating the frame token. --- rfb_utils/display_utils.py | 8 ----- rfb_utils/texture_utils.py | 44 +++++++++---------------- rman_handlers/__init__.py | 26 +++++++++++---- rman_operators/rman_operators_rib.py | 1 - rman_operators/rman_operators_view3d.py | 1 - rman_render.py | 12 ++----- rman_scene_sync.py | 2 -- rman_spool.py | 4 +++ rman_translators/rman_translator.py | 1 - rman_ui/rman_ui_viewport.py | 2 +- 10 files changed, 42 insertions(+), 59 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 158c9603..f1ca5bdb 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -128,7 +128,6 @@ def _add_stylized_channels(dspys_dict, dspy_drv, rman_scene, expandTokens): if expandTokens: filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, asFilePath=True) dspys_dict['displays'][dspy_name] = { @@ -205,7 +204,6 @@ def _set_blender_dspy_dict(layer, dspys_dict, dspy_drv, rman_scene, expandTokens if expandTokens: filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, asFilePath=True) dspys_dict['displays']['beauty'] = { 'driverNode': display_driver, @@ -245,7 +243,6 @@ def _set_blender_dspy_dict(layer, dspys_dict, dspy_drv, rman_scene, expandTokens token_dict = {'aov': name} filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, token_dict=token_dict, asFilePath=True) if doit: @@ -397,7 +394,6 @@ def _set_rman_dspy_dict(rm_rl, dspys_dict, dspy_drv, rman_scene, expandTokens, d token_dict = {'aov': aov.name} filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, token_dict=token_dict, asFilePath=True) @@ -414,7 +410,6 @@ def _set_rman_dspy_dict(rm_rl, dspys_dict, dspy_drv, rman_scene, expandTokens, d if expandTokens: filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, asFilePath=True) else: filePath = rm.path_aov_image_output @@ -422,7 +417,6 @@ def _set_rman_dspy_dict(rm_rl, dspys_dict, dspy_drv, rman_scene, expandTokens, d token_dict = {'aov': aov.name} filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, token_dict=token_dict, asFilePath=True) @@ -552,7 +546,6 @@ def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens, if expandTokens: filePath = string_utils.expand_string(filePath, display=display_driver, - frame=rman_scene.bl_frame_current, asFilePath=True) dspys_dict['displays']['holdoutMatte'] = { @@ -740,7 +733,6 @@ def export_metadata(scene, params, camera_name=None): rm = scene.renderman world = scene.world output_dir = string_utils.expand_string(rm.path_rib_output, - frame=scene.frame_current, asFilePath=True) output_dir = os.path.dirname(output_dir) statspath=os.path.join(output_dir, 'stats.%04d.xml' % scene.frame_current) diff --git a/rfb_utils/texture_utils.py b/rfb_utils/texture_utils.py index ffa95d23..5b7ce971 100644 --- a/rfb_utils/texture_utils.py +++ b/rfb_utils/texture_utils.py @@ -96,10 +96,7 @@ def get_ext_list(self): return ext_list def host_token_resolver_func(self, outpath): - if self.rman_scene: - outpath = string_utils.expand_string(outpath, frame=self.rman_scene.bl_frame_current, asFilePath=True) - else: - outpath = string_utils.expand_string(outpath, asFilePath=True) + outpath = string_utils.expand_string(outpath, asFilePath=True) return outpath def done_callback(self, nodeID, txfile): @@ -123,16 +120,10 @@ def get_output_tex(self, txfile): ''' if txfile.state in (txmanager.STATE_EXISTS, txmanager.STATE_IS_TEX): output_tex = txfile.get_output_texture() - if self.rman_scene: - output_tex = string_utils.expand_string(output_tex, frame=self.rman_scene.bl_frame_current, asFilePath=True) - else: - output_tex = string_utils.expand_string(output_tex, asFilePath=True) + output_tex = string_utils.expand_string(output_tex, asFilePath=True) elif txfile.state == txmanager.STATE_INPUT_MISSING: output_tex = txfile.input_image - if self.rman_scene: - output_tex = string_utils.expand_string(output_tex, frame=self.rman_scene.bl_frame_current, asFilePath=True) - else: - output_tex = string_utils.expand_string(output_tex, asFilePath=True) + output_tex = string_utils.expand_string(output_tex, asFilePath=True) else: output_tex = self.txmanager.get_placeholder_tex() @@ -229,10 +220,7 @@ def is_file_src_tex(self, node, prop_name): return False def does_file_exist(self, file_path): - if self.rman_scene: - outpath = string_utils.expand_string(file_path, frame=self.rman_scene.bl_frame_current, asFilePath=True) - else: - outpath = string_utils.expand_string(file_path, asFilePath=True) + outpath = string_utils.expand_string(file_path, asFilePath=True) if os.path.exists(outpath): return True @@ -433,19 +421,17 @@ def txmanager_pre_save_cb(bl_scene): return get_txmanager().txmanager.save_state() -@persistent -def depsgraph_handler(bl_scene, depsgraph): - for update in depsgraph.updates: - id = update.id - # check new linked in materials - if id.library: - link_file_handler(id) - continue - # check if nodes were renamed - elif isinstance(id, bpy.types.Object): - check_node_rename(id) - elif isinstance(id, bpy.types.Material): - check_node_rename(id) +def depsgraph_handler(depsgraph_update, depsgraph): + id = depsgraph_update.id + # check new linked in materials + if id.library: + link_file_handler(id) + return + # check if nodes were renamed + elif isinstance(id, bpy.types.Object): + check_node_rename(id) + elif isinstance(id, bpy.types.Material): + check_node_rename(id) def check_node_rename(id): nodes_list = list() diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index b1cf5961..e3b93f61 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -38,8 +38,14 @@ def rman_save_post(bl_scene): texture_utils.txmanager_pre_save_cb(bl_scene) @persistent -def texture_despgraph_handler(bl_scene, depsgraph): - texture_utils.depsgraph_handler(bl_scene, depsgraph) +def frame_change_post(bl_scene): + # update frame number + string_utils.update_frame_token(bl_scene.frame_current) + +@persistent +def despgraph_post_handler(bl_scene, depsgraph): + for update in depsgraph.updates: + texture_utils.depsgraph_handler(update, depsgraph) @persistent def render_pre(bl_scene): @@ -110,9 +116,12 @@ def register(): if rman_save_post not in bpy.app.handlers.save_post: bpy.app.handlers.save_post.append(rman_save_post) - # texture_depsgraph_update_post handler - if texture_despgraph_handler not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(texture_despgraph_handler) + # depsgraph_update_post handler + if despgraph_post_handler not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(despgraph_post_handler) + + if frame_change_post not in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.append(frame_change_post) if render_pre not in bpy.app.handlers.render_pre: bpy.app.handlers.render_pre.append(render_pre) @@ -138,8 +147,11 @@ def unregister(): if rman_save_post in bpy.app.handlers.save_post: bpy.app.handlers.save_post.remove(rman_save_post) - if texture_despgraph_handler in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(texture_despgraph_handler) + if despgraph_post_handler in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(despgraph_post_handler) + + if frame_change_post in bpy.app.handlers.frame_change_post: + bpy.app.handlers.frame_change_post.remove(frame_change_post) if render_pre in bpy.app.handlers.render_pre: bpy.app.handlers.render_pre.remove(render_pre) diff --git a/rman_operators/rman_operators_rib.py b/rman_operators/rman_operators_rib.py index d9de7962..cedb1e75 100644 --- a/rman_operators/rman_operators_rib.py +++ b/rman_operators/rman_operators_rib.py @@ -32,7 +32,6 @@ def invoke(self, context, event=None): rm.rib_format = format_prev_val rib_output = string_utils.expand_string(rm.path_rib_output, - frame=scene.frame_current, asFilePath=True) filepath_utils.view_file(rib_output) diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index efc22ec7..28522e9d 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -512,7 +512,6 @@ def execute(self, context): scene = context.scene rm = scene.renderman output_dir = string_utils.expand_string(rm.path_rib_output, - frame=scene.frame_current, asFilePath=True) output_dir = os.path.dirname(output_dir) bpy.ops.wm.url_open( diff --git a/rman_render.py b/rman_render.py index 01ca2bde..30634578 100644 --- a/rman_render.py +++ b/rman_render.py @@ -509,6 +509,7 @@ def _call_brickmake_for_selected(self): args.append(ptc_file) args.append(bkm_file) subprocess.run(args) + string_utils.update_frame_token(self.bl_scene.frame_current) def _check_prman_license(self): if not envconfig().is_valid_license: @@ -751,7 +752,6 @@ def start_external_render(self, depsgraph): self.rman_scene_sync.batch_update_scene(bpy.context, depsgraph) rib_output = string_utils.expand_string(rm.path_rib_output, - frame=frame, asFilePath=True) self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) @@ -780,7 +780,6 @@ def start_external_render(self, depsgraph): self.rman_is_exporting = False rib_output = string_utils.expand_string(rm.path_rib_output, - frame=frame, asFilePath=True) self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) self.sgmngr.DeleteScene(self.sg_scene) @@ -816,7 +815,6 @@ def start_external_render(self, depsgraph): self.rman_scene.export_for_final_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) self.rman_is_exporting = False rib_output = string_utils.expand_string(rm.path_rib_output, - frame=bl_scene.frame_current, asFilePath=True) rfb_log().debug("Writing to RIB: %s..." % rib_output) @@ -931,7 +929,6 @@ def start_external_bake_render(self, depsgraph): self.rman_scene.export_for_bake_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) self.rman_is_exporting = False rib_output = string_utils.expand_string(rm.path_rib_output, - frame=frame, asFilePath=True) self.sg_scene.Render("rib %s %s" % (rib_output, rib_options)) except Exception as e: @@ -961,7 +958,6 @@ def start_external_bake_render(self, depsgraph): self.rman_scene.export_for_bake_render(depsgraph, self.sg_scene, bl_view_layer, is_external=True) self.rman_is_exporting = False rib_output = string_utils.expand_string(rm.path_rib_output, - frame=bl_scene.frame_current, asFilePath=True) rfb_log().debug("Writing to RIB: %s..." % rib_output) @@ -1153,7 +1149,6 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.rman_scene.export_for_rib_selection(context, self.sg_scene) self.rman_is_exporting = False rib_output = string_utils.expand_string(rib_path, - frame=frame, asFilePath=True) cmd = 'rib ' + rib_output + ' -archive' cmd = cmd + ' -bbox ' + transform_utils.get_world_bounding_box(context.selected_objects) @@ -1177,7 +1172,6 @@ def start_export_rib_selected(self, context, rib_path, export_materials=True, ex self.rman_scene.export_for_rib_selection(context, self.sg_scene) self.rman_is_exporting = False rib_output = string_utils.expand_string(rib_path, - frame=bl_scene.frame_current, asFilePath=True) cmd = 'rib ' + rib_output + ' -archive' cmd = cmd + ' -bbox ' + transform_utils.get_world_bounding_box(context.selected_objects) @@ -1447,7 +1441,7 @@ def _get_buffer(self, width, height, image_num=0, num_channels=-1, raw_buffer=Fa return None @time_this - def save_viewport_snapshot(self, frame=1): + def save_viewport_snapshot(self): if not self.rman_is_viewport_rendering: return @@ -1456,7 +1450,7 @@ def save_viewport_snapshot(self, frame=1): height = int(self.viewport_res_y * res_mult) nm = 'rman_viewport_snapshot__%d.exr' % len(bpy.data.images) - nm = string_utils.expand_string(nm, frame=frame) + nm = string_utils.expand_string(nm) if hasattr(ice, 'FromArray'): buffer = self._get_buffer(width, height, as_flat=False, raw_buffer=True) if buffer is None: diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 70c58b5e..a800187a 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -118,7 +118,6 @@ def scene_updated(self): # are marked as frame sensitive rfb_log().debug("Frame changed: %d -> %d" % (self.rman_scene.bl_frame_current, self.rman_scene.bl_scene.frame_current)) self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current - string_utils.update_frame_token(self.rman_scene.bl_frame_current) self.frame_number_changed = True # check for frame sensitive objects @@ -500,7 +499,6 @@ def check_shader_nodetree(self, dps_update): @time_this def batch_update_scene(self, context, depsgraph): self.rman_scene.bl_frame_current = self.rman_scene.bl_scene.frame_current - string_utils.update_frame_token(self.rman_scene.bl_frame_current) self.rman_updates = dict() self.num_instances_changed = False diff --git a/rman_spool.py b/rman_spool.py index 71fa5cda..c90ea9b8 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -340,6 +340,7 @@ def blender_batch_render(self, bl_filename): rm = scene.renderman frame_begin = self.bl_scene.frame_start frame_end = self.bl_scene.frame_end + frame_current = self.bl_scene.frame_current by = self.bl_scene.frame_step chunk = min(rm.bl_batch_frame_chunk, frame_end)-1 @@ -420,6 +421,7 @@ def blender_batch_render(self, bl_filename): return self.spool(job, jobfile) + string_utils.update_frame_token(frame_current) def batch_render(self): @@ -428,6 +430,7 @@ def batch_render(self): rm = scene.renderman frame_begin = self.bl_scene.frame_start frame_end = self.bl_scene.frame_end + frame_current = self.bl_scene.frame_current by = self.bl_scene.frame_step if not rm.external_animation: @@ -492,6 +495,7 @@ def batch_render(self): return self.spool(job, jobfile) + string_utils.update_frame_token(frame_current) def spool(self, job, jobfile): diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index f625d66a..a420163f 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -243,7 +243,6 @@ def export_object_attributes(self, ob, rman_sg_node, remove=True): if tokens[1] != '': filePath = tokens[0] filePath = string_utils.expand_string(filePath, - frame=self.rman_scene.bl_frame_current, asFilePath=True) attrs.SetString('user:bake_filename_attr', filePath) diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 227cdab3..160a3db8 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -190,7 +190,7 @@ class PRMAN_OT_Viewport_Snapshot(bpy.types.Operator): def execute(self, context): rman_render = RmanRender.get_rman_render() scene = context.scene - rman_render.save_viewport_snapshot(frame=scene.frame_current) + rman_render.save_viewport_snapshot() return {"FINISHED"} From a32f3753d0a2b0448492cb5db6452edc4aa039b5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 9 Jun 2022 11:54:43 -0700 Subject: [PATCH 157/278] Fix index for our collection operators when a new entry was added. --- rman_operators/rman_operators_collections.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rman_operators/rman_operators_collections.py b/rman_operators/rman_operators_collections.py index 6f3ff34a..3745b9e1 100644 --- a/rman_operators/rman_operators_collections.py +++ b/rman_operators/rman_operators_collections.py @@ -53,7 +53,7 @@ def invoke(self, context, event): if self.properties.action == 'ADD': dflt_name = self.properties.defaultname collection.add() - index += 1 + index = len(collection)-1 setattr(rm, coll_idx, index) if dflt_name != '': for coll in collection: @@ -107,7 +107,7 @@ def invoke(self, context, event): if self.properties.action == 'ADD': dflt_name = self.properties.defaultname collection.add() - index += 1 + index = len(collection)-1 setattr(rm, coll_idx, index) i = 0 if dflt_name != '': @@ -163,7 +163,7 @@ def invoke(self, context, event): if self.properties.action == 'ADD': dflt_name = self.properties.defaultname collection.add() - index += 1 + index = len(collection)-1 setattr(rm, coll_idx, index) i = 0 if dflt_name != '': @@ -227,7 +227,7 @@ def invoke(self, context, event): if coll.name == dflt_name: dflt_name = '%s_NEW' % dflt_name collection.add() - index += 1 + index = len(collection)-1 setattr(rm, coll_idx, index) try: collection[-1].name = dflt_name @@ -285,7 +285,7 @@ def invoke(self, context, event): if coll.name == dflt_name: dflt_name = '%s_NEW' % dflt_name collection.add() - index += 1 + index = len(collection)-1 setattr(rm, coll_idx, index) collection[-1].name = dflt_name @@ -888,7 +888,7 @@ def execute(self, context): connectable = False if self.action == 'ADD': elem = collection.add() - index += 1 + index = len(collection)-1 setattr(node, self.collection_index, index) elem.name = '%s[%d]' % (self.param_name, len(collection)-1) elem.type = self.elem_type From 2f765b9eda8a3ea98477228cb94892d6d4149a9b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 9 Jun 2022 13:16:33 -0700 Subject: [PATCH 158/278] * When the addon is loading, double check if the node_arrange addon is also loaded * Add the node_arrange operators to our right click context menu in the node editor. --- __init__.py | 11 ++++++++++- rman_ui/rman_ui_header_panels.py | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index 569bd508..b97bca74 100644 --- a/__init__.py +++ b/__init__.py @@ -25,7 +25,7 @@ import bpy import bgl import blf -import time +import addon_utils from .rfb_utils.prefs_utils import get_pref from .rfb_utils import string_utils @@ -261,6 +261,14 @@ def _draw_pixels(self, context, depsgraph): self.unbind_display_space_shader() bgl.glDisable(bgl.GL_BLEND) +def load_node_arrange(): + ''' + Make sure that the node_arrange addon is enabled + ''' + + if addon_utils.check('node_arrange')[1] is False: + addon_utils.enable('node_arrange') + def load_addon(): global __RMAN_ADDON_LOADED__ @@ -303,6 +311,7 @@ def register(): from . import preferences preferences.register() load_addon() + load_node_arrange() def unregister(): global __RMAN_ADDON_LOADED__ diff --git a/rman_ui/rman_ui_header_panels.py b/rman_ui/rman_ui_header_panels.py index 00544bb3..a0c4c9cf 100644 --- a/rman_ui/rman_ui_header_panels.py +++ b/rman_ui/rman_ui_header_panels.py @@ -38,6 +38,11 @@ def poll(cls, context): rd = context.scene.render return rd.engine == 'PRMAN_RENDER' + def add_arrange_op(self, layout): + rman_icon = rfb_icons.get_icon('rman_graph') + layout.operator('node.button', icon_value=rman_icon.icon_id) + layout.operator('node.na_align_nodes', icon_value=rman_icon.icon_id) + def draw(self, context): layout = self.layout @@ -66,13 +71,18 @@ def draw(self, context): if rman_output_node.solo_node_name != '': op = layout.operator('node.rman_set_node_solo', text='Reset Solo', icon='FILE_REFRESH') - op.refresh_solo = True + op.refresh_solo = True + + self.add_arrange_op(layout) elif type(context.space_data.id) == bpy.types.World: if not context.space_data.id.renderman.use_renderman_node: layout.operator( - 'material.rman_add_rman_nodetree', text="Add RenderMan Nodes").idtype = "world" - + 'material.rman_add_rman_nodetree', text="Add RenderMan Nodes").idtype = "world" + else: + self.add_arrange_op(layout) + else: + self.add_arrange_op(layout) class NODE_HT_DrawRenderHeaderNode(bpy.types.Header): ''' From 0849e64eaa2ed9c0e114e0926004f6a62be347de Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 14 Jun 2022 16:00:28 -0700 Subject: [PATCH 159/278] Move our PRManRender render engine class into its own file (rman_engine.py). This is consistent with how other render engine addons structure their code. --- __init__.py | 241 +------------------------------------------------ rman_engine.py | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 237 deletions(-) create mode 100644 rman_engine.py diff --git a/__init__.py b/__init__.py index b97bca74..04c892d5 100644 --- a/__init__.py +++ b/__init__.py @@ -22,16 +22,10 @@ # # # ##### END MIT LICENSE BLOCK ##### -import bpy -import bgl -import blf import addon_utils -from .rfb_utils.prefs_utils import get_pref -from .rfb_utils import string_utils from .rfb_logger import rfb_log from .rfb_utils.envconfig_utils import envconfig -from .rman_constants import RFB_FLOAT3 bl_info = { "name": "RenderMan For Blender", @@ -46,221 +40,6 @@ __RMAN_ADDON_LOADED__ = False -class PRManRender(bpy.types.RenderEngine): - bl_idname = 'PRMAN_RENDER' - bl_label = "RenderMan" - bl_use_preview = False # Turn off preview renders - bl_use_save_buffers = True - bl_use_shading_nodes = True # We support shading nodes - bl_use_shading_nodes_custom = False - bl_use_eevee_viewport = True # Use Eevee for look dev viewport mode - bl_use_postprocess = True - - def __init__(self): - from . import rman_render - self.rman_render = rman_render.RmanRender.get_rman_render() - self.export_failed = None - if self.is_preview and self.rman_render.rman_swatch_render_running: - # if a preview render is requested and a swatch render is - # already in progress, ignore this render request - return - if self.rman_render.rman_interactive_running: - # If IPR is already running, just return. - # We report an error in render() if this is a render attempt - return - - def __del__(self): - try: - from . import rman_render - except ModuleNotFoundError: - return - - rr = rman_render.RmanRender.get_rman_render() - try: - if self.is_preview: - # If this was a preview render, return - return - except: - pass - - if rr.rman_running: - if rr.rman_interactive_running: - rfb_log().debug("Stop interactive render.") - rr.rman_is_live_rendering = False - elif rr.is_regular_rendering(): - rfb_log().debug("Stop render.") - rr.stop_render(stop_draw_thread=False) - - def update(self, data, depsgraph): - pass - - def view_update(self, context, depsgraph): - ''' - For viewport renders. Blender calls view_update when starting viewport renders - and/or something changes in the scene. - ''' - - # check if we are already doing a regular render - if self.rman_render.is_regular_rendering(): - return - - if self.export_failed: - return - - if self.rman_render.is_ipr_to_it(): - # if we are already IPRing to "it", stop the render - self.rman_render.stop_render(stop_draw_thread=False) - - # if interactive rendering has not started, start it - if not self.rman_render.rman_interactive_running and self.rman_render.sg_scene is None: - self.rman_render.bl_engine = self - self.rman_render.rman_scene.ipr_render_into = 'blender' - if not self.rman_render.start_interactive_render(context, depsgraph): - self.export_failed = True - return - self.export_failed = False - - if self.rman_render.rman_interactive_running and not self.rman_render.rman_license_failed: - self.rman_render.update_scene(context, depsgraph) - - def view_draw(self, context, depsgraph): - ''' - For viewport renders. Blender calls view_draw whenever it redraws the 3D viewport. - This is where we check for camera moves and draw pxiels from our - Blender display driver. - ''' - if self.export_failed: - return - if self.rman_render.rman_interactive_running and not self.rman_render.rman_license_failed: - self.rman_render.update_view(context, depsgraph) - - self._draw_pixels(context, depsgraph) - - def _increment_version_tokens(self, external_render=False): - bl_scene = bpy.context.scene - vi = get_pref('rman_scene_version_increment', default='MANUALLY') - ti = get_pref('rman_scene_take_increment', default='MANUALLY') - - if (vi == 'RENDER' and not external_render) or (vi == 'BATCH_RENDER' and external_render): - bl_scene.renderman.version_token += 1 - string_utils.set_var('version', bl_scene.renderman.version_token) - - if (ti == 'RENDER' and not external_render) or (ti == 'BATCH_RENDER' and external_render): - bl_scene.renderman.take_token += 1 - string_utils.set_var('take', bl_scene.renderman.take_token) - - def update_render_passes(self, scene=None, renderlayer=None): - # this method allows us to add our AOVs as ports to the RenderLayer node - # in the compositor. - - from .rfb_utils import display_utils - if self.is_preview: - return - - if self.rman_render.rman_render_into != 'blender': - return - - self.rman_render.rman_scene.bl_scene = scene - dspy_dict = display_utils.get_dspy_dict(self.rman_render.rman_scene, include_holdouts=False) - self.register_pass(scene, renderlayer, "Combined", 4, "RGBA", 'COLOR') - for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): - if i == 0: - continue - dspy = dspy_dict['displays'][dspy_nm] - dspy_chan = dspy['params']['displayChannels'][0] - chan_info = dspy_dict['channels'][dspy_chan] - chan_type = chan_info['channelType']['value'] - - if chan_type == 'color': - self.register_pass(scene, renderlayer, dspy_nm, 3, "RGB", 'COLOR') - elif chan_type in ['vector', 'normal', 'point']: - self.register_pass(scene, renderlayer, dspy_nm, 3, "XYZ", 'VECTOR') - else: - self.register_pass(scene, renderlayer, dspy_nm, 1, "Z", 'VALUE') - - def render(self, depsgraph): - ''' - Main render entry point. Blender calls this when doing final renders or preview renders. - ''' - - bl_scene = depsgraph.scene_eval - rm = bl_scene.renderman - baking = (rm.hider_type in ['BAKE', 'BAKE_BRICKMAP_SELECTED']) - - if self.rman_render.rman_interactive_running: - # report an error if a render is trying to start while IPR is running - if self.is_preview and get_pref('rman_do_preview_renders', False): - #self.report({'ERROR'}, 'Cannot start a preview render when IPR is running') - rfb_log().debug('Cannot start a preview render when IPR is running') - pass - elif not self.is_preview: - self.report({'ERROR'}, 'Cannot start a render when IPR is running') - return - elif self.is_preview: - # double check we're not already viewport rendering - if self.rman_render.rman_interactive_running: - if get_pref('rman_do_preview_renders', False): - rfb_log().error("Cannot preview render while viewport rendering.") - return - if not get_pref('rman_do_preview_renders', False): - # user has turned off preview renders, just load the placeholder image - self.rman_render.bl_scene = depsgraph.scene_eval - #self.rman_render._load_placeholder_image() - return - if self.rman_render.rman_swatch_render_running: - return - self.rman_render.bl_engine = self - self.rman_render.start_swatch_render(depsgraph) - elif baking: - self.rman_render.bl_engine = self - if rm.enable_external_rendering: - self.rman_render.start_external_bake_render(depsgraph) - elif not self.rman_render.start_bake_render(depsgraph, for_background=bpy.app.background): - return - elif rm.enable_external_rendering: - self.rman_render.bl_engine = self - self.rman_render.start_external_render(depsgraph) - self._increment_version_tokens(external_render=True) - else: - for_background = bpy.app.background - self.rman_render.bl_engine = self - if not self.rman_render.start_render(depsgraph, for_background=for_background): - return - if not for_background: - self._increment_version_tokens(external_render=False) - - def _draw_pixels(self, context, depsgraph): - - scene = depsgraph.scene - w = context.region.width - h = context.region.height - - if self.rman_render.rman_license_failed: - pos_x = w / 2 - 100 - pos_y = 20 - blf.enable(0, blf.SHADOW) - blf.shadow_offset(0, 1, -1) - blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) - blf.size(0, 32, 36) - blf.position(0, pos_x, pos_y, 0) - blf.color(0, 1.0, 0.0, 0.0, 1.0) - blf.draw(0, "%s" % (self.rman_render.rman_license_failed_message)) - blf.disable(0, blf.SHADOW) - return - - if not self.rman_render.rman_is_viewport_rendering: - return - - # Bind shader that converts from scene linear to display space, - bgl.glEnable(bgl.GL_BLEND) - bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA) - self.bind_display_space_shader(scene) - - self.rman_render.draw_pixels(w, h) - - self.unbind_display_space_shader() - bgl.glDisable(bgl.GL_BLEND) - def load_node_arrange(): ''' Make sure that the node_arrange addon is enabled @@ -282,6 +61,7 @@ def load_addon(): from . import rman_handlers from . import rfb_translations from . import rman_stats + from . import rman_engine rman_config.register() rman_properties.pre_register() @@ -293,6 +73,7 @@ def load_addon(): rman_handlers.register() rfb_translations.register() rman_stats.register() + rman_engine.register() __RMAN_ADDON_LOADED__ = True @@ -300,14 +81,7 @@ def load_addon(): rfb_log().error( "Error loading addon. Correct RMANTREE setting in addon preferences.") -classes = [ - PRManRender, -] - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - +def register(): from . import preferences preferences.register() load_addon() @@ -328,11 +102,4 @@ def unregister(): rman_operators.unregister() rfb_translations.unregister() rman_stats.unregister() - - for cls in classes: - try: - bpy.utils.unregister_class(cls) - except RuntimeError: - rfb_log().debug('Could not unregister class: %s' % str(cls)) - pass - + rman_engine.unregister() diff --git a/rman_engine.py b/rman_engine.py new file mode 100644 index 00000000..d74f3995 --- /dev/null +++ b/rman_engine.py @@ -0,0 +1,238 @@ +import bpy +import bgl +import blf + +from .rfb_utils.prefs_utils import get_pref +from .rfb_utils import string_utils +from .rfb_logger import rfb_log + +class PRManRender(bpy.types.RenderEngine): + bl_idname = 'PRMAN_RENDER' + bl_label = "RenderMan" + bl_use_preview = False # Turn off preview renders + bl_use_save_buffers = True + bl_use_shading_nodes = True # We support shading nodes + bl_use_shading_nodes_custom = False + bl_use_eevee_viewport = True # Use Eevee for look dev viewport mode + bl_use_postprocess = True + + def __init__(self): + from . import rman_render + self.rman_render = rman_render.RmanRender.get_rman_render() + self.export_failed = None + if self.is_preview and self.rman_render.rman_swatch_render_running: + # if a preview render is requested and a swatch render is + # already in progress, ignore this render request + return + if self.rman_render.rman_interactive_running: + # If IPR is already running, just return. + # We report an error in render() if this is a render attempt + return + + def __del__(self): + try: + from . import rman_render + except ModuleNotFoundError: + return + + rr = rman_render.RmanRender.get_rman_render() + try: + if self.is_preview: + # If this was a preview render, return + return + except: + pass + + if rr.rman_running: + if rr.rman_interactive_running: + rfb_log().debug("Stop interactive render.") + rr.rman_is_live_rendering = False + elif rr.is_regular_rendering(): + rfb_log().debug("Stop render.") + rr.stop_render(stop_draw_thread=False) + + def update(self, data, depsgraph): + pass + + def view_update(self, context, depsgraph): + ''' + For viewport renders. Blender calls view_update when starting viewport renders + and/or something changes in the scene. + ''' + + # check if we are already doing a regular render + if self.rman_render.is_regular_rendering(): + return + + if self.export_failed: + return + + if self.rman_render.is_ipr_to_it(): + # if we are already IPRing to "it", stop the render + self.rman_render.stop_render(stop_draw_thread=False) + + # if interactive rendering has not started, start it + if not self.rman_render.rman_interactive_running and self.rman_render.sg_scene is None: + self.rman_render.bl_engine = self + self.rman_render.rman_scene.ipr_render_into = 'blender' + if not self.rman_render.start_interactive_render(context, depsgraph): + self.export_failed = True + return + self.export_failed = False + + if self.rman_render.rman_interactive_running and not self.rman_render.rman_license_failed: + self.rman_render.update_scene(context, depsgraph) + + def view_draw(self, context, depsgraph): + ''' + For viewport renders. Blender calls view_draw whenever it redraws the 3D viewport. + This is where we check for camera moves and draw pxiels from our + Blender display driver. + ''' + if self.export_failed: + return + if self.rman_render.rman_interactive_running and not self.rman_render.rman_license_failed: + self.rman_render.update_view(context, depsgraph) + + self._draw_pixels(context, depsgraph) + + def _increment_version_tokens(self, external_render=False): + bl_scene = bpy.context.scene + vi = get_pref('rman_scene_version_increment', default='MANUALLY') + ti = get_pref('rman_scene_take_increment', default='MANUALLY') + + if (vi == 'RENDER' and not external_render) or (vi == 'BATCH_RENDER' and external_render): + bl_scene.renderman.version_token += 1 + string_utils.set_var('version', bl_scene.renderman.version_token) + + if (ti == 'RENDER' and not external_render) or (ti == 'BATCH_RENDER' and external_render): + bl_scene.renderman.take_token += 1 + string_utils.set_var('take', bl_scene.renderman.take_token) + + def update_render_passes(self, scene=None, renderlayer=None): + # this method allows us to add our AOVs as ports to the RenderLayer node + # in the compositor. + + from .rfb_utils import display_utils + if self.is_preview: + return + + if self.rman_render.rman_render_into != 'blender': + return + + self.rman_render.rman_scene.bl_scene = scene + dspy_dict = display_utils.get_dspy_dict(self.rman_render.rman_scene, include_holdouts=False) + self.register_pass(scene, renderlayer, "Combined", 4, "RGBA", 'COLOR') + for i, dspy_nm in enumerate(dspy_dict['displays'].keys()): + if i == 0: + continue + dspy = dspy_dict['displays'][dspy_nm] + dspy_chan = dspy['params']['displayChannels'][0] + chan_info = dspy_dict['channels'][dspy_chan] + chan_type = chan_info['channelType']['value'] + + if chan_type == 'color': + self.register_pass(scene, renderlayer, dspy_nm, 3, "RGB", 'COLOR') + elif chan_type in ['vector', 'normal', 'point']: + self.register_pass(scene, renderlayer, dspy_nm, 3, "XYZ", 'VECTOR') + else: + self.register_pass(scene, renderlayer, dspy_nm, 1, "Z", 'VALUE') + + def render(self, depsgraph): + ''' + Main render entry point. Blender calls this when doing final renders or preview renders. + ''' + + bl_scene = depsgraph.scene_eval + rm = bl_scene.renderman + baking = (rm.hider_type in ['BAKE', 'BAKE_BRICKMAP_SELECTED']) + + if self.rman_render.rman_interactive_running: + # report an error if a render is trying to start while IPR is running + if self.is_preview and get_pref('rman_do_preview_renders', False): + #self.report({'ERROR'}, 'Cannot start a preview render when IPR is running') + rfb_log().debug('Cannot start a preview render when IPR is running') + pass + elif not self.is_preview: + self.report({'ERROR'}, 'Cannot start a render when IPR is running') + return + elif self.is_preview: + # double check we're not already viewport rendering + if self.rman_render.rman_interactive_running: + if get_pref('rman_do_preview_renders', False): + rfb_log().error("Cannot preview render while viewport rendering.") + return + if not get_pref('rman_do_preview_renders', False): + # user has turned off preview renders, just load the placeholder image + self.rman_render.bl_scene = depsgraph.scene_eval + #self.rman_render._load_placeholder_image() + return + if self.rman_render.rman_swatch_render_running: + return + self.rman_render.bl_engine = self + self.rman_render.start_swatch_render(depsgraph) + elif baking: + self.rman_render.bl_engine = self + if rm.enable_external_rendering: + self.rman_render.start_external_bake_render(depsgraph) + elif not self.rman_render.start_bake_render(depsgraph, for_background=bpy.app.background): + return + elif rm.enable_external_rendering: + self.rman_render.bl_engine = self + self.rman_render.start_external_render(depsgraph) + self._increment_version_tokens(external_render=True) + else: + for_background = bpy.app.background + self.rman_render.bl_engine = self + if not self.rman_render.start_render(depsgraph, for_background=for_background): + return + if not for_background: + self._increment_version_tokens(external_render=False) + + def _draw_pixels(self, context, depsgraph): + + scene = depsgraph.scene + w = context.region.width + h = context.region.height + + if self.rman_render.rman_license_failed: + pos_x = w / 2 - 100 + pos_y = 20 + blf.enable(0, blf.SHADOW) + blf.shadow_offset(0, 1, -1) + blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) + blf.size(0, 32, 36) + blf.position(0, pos_x, pos_y, 0) + blf.color(0, 1.0, 0.0, 0.0, 1.0) + blf.draw(0, "%s" % (self.rman_render.rman_license_failed_message)) + blf.disable(0, blf.SHADOW) + return + + if not self.rman_render.rman_is_viewport_rendering: + return + + # Bind shader that converts from scene linear to display space, + bgl.glEnable(bgl.GL_BLEND) + bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA) + self.bind_display_space_shader(scene) + + self.rman_render.draw_pixels(w, h) + + self.unbind_display_space_shader() + bgl.glDisable(bgl.GL_BLEND) + +classes = [ + PRManRender, +] + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + +def unregister(): + for cls in classes: + try: + bpy.utils.unregister_class(cls) + except RuntimeError: + rfb_log().debug('Could not unregister class: %s' % str(cls)) + pass \ No newline at end of file From 9fce9aa8f131c3b17f5a86cae60c4acca49e4203 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 20 Jun 2022 07:36:18 -0700 Subject: [PATCH 160/278] Sync new OCIO change from Perforce. --- rman_scene.py | 3185 +++++++++++++++++++++++++------------------------ 1 file changed, 1593 insertions(+), 1592 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index e4252b66..2385411d 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -1,1592 +1,1593 @@ -# Translators -from .rman_translators.rman_camera_translator import RmanCameraTranslator -from .rman_translators.rman_light_translator import RmanLightTranslator -from .rman_translators.rman_lightfilter_translator import RmanLightFilterTranslator -from .rman_translators.rman_mesh_translator import RmanMeshTranslator -from .rman_translators.rman_material_translator import RmanMaterialTranslator -from .rman_translators.rman_hair_translator import RmanHairTranslator -from .rman_translators.rman_group_translator import RmanGroupTranslator -from .rman_translators.rman_points_translator import RmanPointsTranslator -from .rman_translators.rman_quadric_translator import RmanQuadricTranslator -from .rman_translators.rman_blobby_translator import RmanBlobbyTranslator -from .rman_translators.rman_particles_translator import RmanParticlesTranslator -from .rman_translators.rman_procedural_translator import RmanProceduralTranslator -from .rman_translators.rman_dra_translator import RmanDraTranslator -from .rman_translators.rman_runprogram_translator import RmanRunProgramTranslator -from .rman_translators.rman_openvdb_translator import RmanOpenVDBTranslator -from .rman_translators.rman_gpencil_translator import RmanGPencilTranslator -from .rman_translators.rman_fluid_translator import RmanFluidTranslator -from .rman_translators.rman_curve_translator import RmanCurveTranslator -from .rman_translators.rman_nurbs_translator import RmanNurbsTranslator -from .rman_translators.rman_volume_translator import RmanVolumeTranslator -from .rman_translators.rman_brickmap_translator import RmanBrickmapTranslator -from .rman_translators.rman_emitter_translator import RmanEmitterTranslator -from .rman_translators.rman_empty_translator import RmanEmptyTranslator -from .rman_translators.rman_alembic_translator import RmanAlembicTranslator - -# utils -from .rfb_utils import object_utils -from .rfb_utils import transform_utils -from .rfb_utils import property_utils -from .rfb_utils import display_utils -from .rfb_utils import string_utils -from .rfb_utils import texture_utils -from .rfb_utils import filepath_utils -from .rfb_utils.envconfig_utils import envconfig -from .rfb_utils import scene_utils -from .rfb_utils.prefs_utils import get_pref -from .rfb_utils import shadergraph_utils -from .rfb_utils import color_manager_blender -from .rfb_utils import scenegraph_utils - -# config -from .rman_config import __RFB_CONFIG_DICT__ as rfb_config -from . import rman_constants - -from .rfb_logger import rfb_log -from .rman_sg_nodes.rman_sg_node import RmanSgNode - -import bpy -import os -import sys - -class RmanScene(object): - ''' - The RmanScene handles translating the Blender scene. - - Attributes: - rman_render (RmanRender) - pointer back to the current RmanRender object - rman () - rman python module - sg_scene (RixSGSCene) - the RenderMan scene graph object - context (bpy.types.Context) - the current Blender context object - depsgraph (bpy.types.Depsgraph) - the Blender dependency graph - bl_scene (bpy.types.Scene) - the current Blender scene object - bl_frame_current (int) - the current Blender frame - bl_view_layer (bpy.types.ViewLayer) - the current Blender view layer - rm_rl (RendermanRenderLayerSettings) - the current rman layer - do_motion_blur (bool) - user requested for motion blur - rman_bake (bool) - user requested a bake render - is_interactive (bool) - whether we are in interactive mode - external_render (bool) - whether we are exporting for external (RIB) renders - is_viewport_render (bool) - whether we are rendering into Blender's viewport - scene_solo_light (bool) - user has solo'd a light (all other lights are muted) - rman_materials (dict) - dictionary of scene's materials - rman_translators (dict) - dictionary of all RmanTranslator(s) - rman_particles (dict) - dictionary of all particle systems used - rman_cameras (dict) - dictionary of all cameras in the scene - obj_hash (dict) - dictionary of hashes to objects ( for object picking ) - moving_objects (dict) - dictionary of objects that are moving/deforming in the scene - motion_steps (set) - the full set of motion steps for the scene, including - overrides from individual objects - main_camera (RmanSgCamera) - pointer to the main scene camera - rman_root_sg_node (RixSGGroup) - the main root RixSceneGraph node - render_default_light (bool) - whether to add a "headlight" light when there are no lights in the scene - world_df_node (RixSGShader) - a display filter shader that represents the world color - default_light (RixSGAnalyticLight) - the default "headlight" light - viewport_render_res_mult (float) - the current render resolution multiplier (for IPR) - num_object_instances (int) - the current number of object instances. This is used during IPR to - track the number of instances between edits. We try to use this to determine - when an object is added or deleted. - num_objects_in_viewlayer (int) - the current number of objects in the current view layer. We're using this - to keep track if an object was removed from a collection - objects_in_viewlayer (list) - the list of objects (bpy.types.Object) in this view layer. - ''' - - def __init__(self, rman_render=None): - self.rman_render = rman_render - self.rman = rman_render.rman - self.sg_scene = None - self.context = None - self.depsgraph = None - self.bl_scene = None - self.bl_frame_current = None - self.bl_view_layer = None - self.rm_rl = None - - self.do_motion_blur = False - self.rman_bake = False - self.is_interactive = False - self.external_render = False - self.is_viewport_render = False - self.is_swatch_render = False - self.scene_solo_light = False - self.scene_any_lights = False - self.is_xpu = False - - self.rman_materials = dict() - self.rman_translators = dict() - self.rman_particles = dict() - self.rman_cameras = dict() - self.obj_hash = dict() - self.moving_objects = dict() - self.rman_prototypes = dict() - - self.motion_steps = set() - self.main_camera = None - self.rman_root_sg_node = None - - self.render_default_light = False - self.world_df_node = None - self.default_light = None - - self.viewport_render_res_mult = 1.0 - self.num_object_instances = 0 - self.num_objects_in_viewlayer = 0 - self.objects_in_viewlayer = list() - - self.ipr_render_into = 'blender' - - self.create_translators() - - - def create_translators(self): - # Create our dictionary of translators. The object type is determined - # by the "_detect_primitive_" function in rfb_utils/object_utils.py - - self.rman_translators['CAMERA'] = RmanCameraTranslator(rman_scene=self) - self.rman_translators['LIGHT'] = RmanLightTranslator(rman_scene=self) - self.rman_translators['LIGHTFILTER'] = RmanLightFilterTranslator(rman_scene=self) - self.rman_translators['MATERIAL'] = RmanMaterialTranslator(rman_scene=self) - self.rman_translators['HAIR'] = RmanHairTranslator(rman_scene=self) - self.rman_translators['GROUP'] = RmanGroupTranslator(rman_scene=self) - self.rman_translators['EMPTY'] = RmanEmptyTranslator(rman_scene=self) - self.rman_translators['EMPTY_INSTANCER'] = RmanEmptyTranslator(rman_scene=self) - self.rman_translators['POINTS'] = RmanPointsTranslator(rman_scene=self) - self.rman_translators['META'] = RmanBlobbyTranslator(rman_scene=self) - self.rman_translators['PARTICLES'] = RmanParticlesTranslator(rman_scene=self) - self.rman_translators['EMITTER'] = RmanEmitterTranslator(rman_scene=self) - self.rman_translators['DYNAMIC_LOAD_DSO'] = RmanProceduralTranslator(rman_scene=self) - self.rman_translators['DELAYED_LOAD_ARCHIVE'] = RmanDraTranslator(rman_scene=self) - self.rman_translators['PROCEDURAL_RUN_PROGRAM'] = RmanRunProgramTranslator(rman_scene=self) - self.rman_translators['OPENVDB'] = RmanOpenVDBTranslator(rman_scene=self) - self.rman_translators['GPENCIL'] = RmanGPencilTranslator(rman_scene=self) - self.rman_translators['MESH'] = RmanMeshTranslator(rman_scene=self) - self.rman_translators['QUADRIC'] = RmanQuadricTranslator(rman_scene=self) - self.rman_translators['FLUID'] = RmanFluidTranslator(rman_scene=self) - self.rman_translators['CURVE'] = RmanCurveTranslator(rman_scene=self) - self.rman_translators['NURBS'] = RmanNurbsTranslator(rman_scene=self) - self.rman_translators['RI_VOLUME'] = RmanVolumeTranslator(rman_scene=self) - self.rman_translators['BRICKMAP'] = RmanBrickmapTranslator(rman_scene=self) - self.rman_translators['ALEMBIC'] = RmanAlembicTranslator(rman_scene=self) - - def _find_renderman_layer(self): - self.rm_rl = None - if self.bl_view_layer.renderman.use_renderman: - self.rm_rl = self.bl_view_layer.renderman - - def reset(self): - # clear out dictionaries etc. - self.rman_materials.clear() - self.rman_particles.clear() - self.rman_cameras.clear() - self.obj_hash.clear() - self.motion_steps = set() - self.moving_objects.clear() - self.rman_prototypes.clear() - - self.main_camera = None - self.render_default_light = False - self.world_df_node = None - self.default_light = None - self.is_xpu = False - self.num_object_instances = 0 - self.num_objects_in_viewlayer = 0 - self.objects_in_viewlayer.clear() - - try: - if self.is_viewport_render: - self.viewport_render_res_mult = float(self.context.scene.renderman.viewport_render_res_mult) - else: - self.viewport_render_res_mult = 1.0 - except AttributeError as err: - rfb_log().debug("Cannot set viewport_render_res_mult: %s" % str(err)) - - - def export_for_final_render(self, depsgraph, sg_scene, bl_view_layer, is_external=False): - self.sg_scene = sg_scene - self.context = bpy.context - self.bl_scene = depsgraph.scene_eval - self.bl_view_layer = bl_view_layer - self._find_renderman_layer() - self.depsgraph = depsgraph - self.external_render = is_external - self.is_interactive = False - self.is_viewport_render = False - self.do_motion_blur = self.bl_scene.renderman.motion_blur - self.export() - - def export_for_bake_render(self, depsgraph, sg_scene, bl_view_layer, is_external=False): - self.sg_scene = sg_scene - self.context = bpy.context - self.bl_scene = depsgraph.scene_eval - self.bl_view_layer = bl_view_layer - self._find_renderman_layer() - self.depsgraph = depsgraph - self.external_render = is_external - self.is_interactive = False - self.is_viewport_render = False - self.do_motion_blur = self.bl_scene.renderman.motion_blur - self.rman_bake = True - - if self.bl_scene.renderman.hider_type == 'BAKE_BRICKMAP_SELECTED': - self.export_bake_brickmap_selected() - else: - self.export_bake_render_scene() - - def export_for_interactive_render(self, context, depsgraph, sg_scene): - self.sg_scene = sg_scene - self.context = context - self.bl_view_layer = context.view_layer - self.bl_scene = depsgraph.scene_eval - self._find_renderman_layer() - self.depsgraph = depsgraph - self.external_render = False - self.is_interactive = True - self.is_viewport_render = False - self.rman_bake = False - - if self.ipr_render_into == 'blender': - self.is_viewport_render = True - - self.do_motion_blur = False - - self.export() - - def export_for_rib_selection(self, context, sg_scene): - self.reset() - self.bl_scene = context.scene - self.bl_frame_current = self.bl_scene.frame_current - self.sg_scene = sg_scene - self.context = context - self.bl_view_layer = context.view_layer - self._find_renderman_layer() - self.rman_bake = False - self.external_render = False - self.is_interactive = False - self.is_viewport_render = False - - self.depsgraph = context.evaluated_depsgraph_get() - self.export_root_sg_node() - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - self.export_data_blocks(selected_objects=True) - - def export_for_swatch_render(self, depsgraph, sg_scene): - self.sg_scene = sg_scene - self.context = bpy.context #None - self.bl_scene = depsgraph.scene_eval - self.depsgraph = depsgraph - self.external_render = False - self.is_interactive = False - self.is_viewport_render = False - self.do_motion_blur = False - self.rman_bake = False - self.is_swatch_render = True - self.export_swatch_render_scene() - - def export(self): - - self.reset() - - self.render_default_light = self.bl_scene.renderman.render_default_light - if sys.platform != "darwin": - self.is_xpu = (self.bl_scene.renderman.renderVariant != 'prman') - - # update variables - string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) - string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) - - self.bl_frame_current = self.bl_scene.frame_current - string_utils.update_frame_token(self.bl_frame_current) - - rfb_log().debug("Creating root scene graph node") - self.export_root_sg_node() - - rfb_log().debug("Calling export_materials()") - #self.export_materials(bpy.data.materials) - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - - # tell the texture manager to start converting any unconverted textures - # normally textures are converted as they are added to the scene - rfb_log().debug("Calling txmake_all()") - texture_utils.get_txmanager().rman_scene = self - texture_utils.get_txmanager().txmake_all(blocking=True) - - self.scene_any_lights = self._scene_has_lights() - - rfb_log().debug("Calling export_data_blocks()") - self.export_data_blocks() - - self.export_searchpaths() - self.export_global_options() - self.export_hider() - self.export_integrator() - - self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) - - # export default light - self.export_defaultlight() - self.main_camera.sg_node.AddChild(self.default_light) - - self.export_displays() - self.export_samplefilters() - self.export_displayfilters() - - if self.do_motion_blur: - rfb_log().debug("Calling export_instances_motion()") - self.export_instances_motion() - - self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) - self.num_object_instances = len(self.depsgraph.object_instances) - visible_objects = getattr(self.context, 'visible_objects', list()) - self.num_objects_in_viewlayer = len(visible_objects) - self.objects_in_viewlayer = [o for o in visible_objects] - - if self.is_interactive: - self.export_viewport_stats() - else: - self.export_stats() - - def export_bake_render_scene(self): - self.reset() - - # update tokens - string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) - string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) - - self.bl_frame_current = self.bl_scene.frame_current - rfb_log().debug("Creating root scene graph node") - self.export_root_sg_node() - - rfb_log().debug("Calling export_materials()") - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - - rfb_log().debug("Calling txmake_all()") - texture_utils.get_txmanager().rman_scene = self - texture_utils.get_txmanager().txmake_all(blocking=True) - - self.scene_any_lights = self._scene_has_lights() - - rm = self.bl_scene.renderman - rman_root_sg_node = self.get_root_sg_node() - attrs = rman_root_sg_node.GetAttributes() - attrs.SetFloat("dice:worlddistancelength", rm.rman_bake_illlum_density) - rman_root_sg_node.SetAttributes(attrs) - - rfb_log().debug("Calling export_data_blocks()") - self.export_data_blocks() - - self.export_searchpaths() - self.export_global_options() - self.export_hider() - self.export_integrator() - self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) - - # export default light - self.export_defaultlight() - self.main_camera.sg_node.AddChild(self.default_light) - - self.export_bake_displays() - self.export_samplefilters() - self.export_displayfilters() - - if self.do_motion_blur: - rfb_log().debug("Calling export_instances_motion()") - self.export_instances_motion() - - options = self.sg_scene.GetOptions() - bake_resolution = int(rm.rman_bake_illlum_res) - options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (bake_resolution, bake_resolution), 2) - self.sg_scene.SetOptions(options) - - def export_bake_brickmap_selected(self): - self.reset() - - # update variables - string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) - string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) - - self.bl_frame_current = self.bl_scene.frame_current - rfb_log().debug("Creating root scene graph node") - self.export_root_sg_node() - - rfb_log().debug("Calling export_materials()") - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - rfb_log().debug("Calling txmake_all()") - texture_utils.get_txmanager().rman_scene = self - texture_utils.get_txmanager().txmake_all(blocking=True) - - self.scene_any_lights = self._scene_has_lights() - - rm = self.bl_scene.renderman - rman_root_sg_node = self.get_root_sg_node() - attrs = rman_root_sg_node.GetAttributes() - attrs.SetFloat("dice:worlddistancelength", rm.rman_bake_illlum_density) - rman_root_sg_node.SetAttributes(attrs) - - self.export_searchpaths() - self.export_global_options() - self.export_hider() - self.export_integrator() - self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) - - # export default light - self.export_defaultlight() - self.main_camera.sg_node.AddChild(self.default_light) - - ob = self.context.active_object - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - objects_needed = [x.original for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] - objects_needed.append(ob.original) - self.export_data_blocks(objects_list=objects_needed) - - self.export_samplefilters() - self.export_displayfilters() - - options = self.sg_scene.GetOptions() - bake_resolution = int(rm.rman_bake_illlum_res) - options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (bake_resolution, bake_resolution), 2) - self.sg_scene.SetOptions(options) - - # Display - display_driver = 'pointcloud' - dspy_chan_Ci = self.rman.SGManager.RixSGDisplayChannel('color', 'Ci') - - self.sg_scene.SetDisplayChannel([dspy_chan_Ci]) - render_output = '%s.ptc' % ob.renderman.bake_filename_attr - render_output = string_utils.expand_string(render_output) - display = self.rman.SGManager.RixSGShader("Display", display_driver, render_output) - display.params.SetString("mode", 'Ci') - self.main_camera.sg_camera_node.SetDisplay(display) - - def export_swatch_render_scene(self): - self.reset() - - # options - options = self.sg_scene.GetOptions() - options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, get_pref('rman_preview_renders_minSamples', default=0)) - options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, get_pref('rman_preview_renders_minSamples', default=1)) - options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, 1) - options.SetString("adaptivemetric", "variance") - scale = 100.0 / self.bl_scene.render.resolution_percentage - w = int(self.bl_scene.render.resolution_x * scale) - h = int(self.bl_scene.render.resolution_y * scale) - options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (w, h), 2) - options.SetFloat(self.rman.Tokens.Rix.k_Ri_PixelVariance, get_pref('rman_preview_renders_pixelVariance', default=0.15)) - options.SetInteger(self.rman.Tokens.Rix.k_limits_threads, -2) - options.SetString(self.rman.Tokens.Rix.k_bucket_order, 'horizontal') - self.sg_scene.SetOptions(options) - - # searchpaths - self.export_searchpaths() - - # integrator - integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrDirectLighting", "integrator") - self.sg_scene.SetIntegrator(integrator_sg) - - # camera - self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) - - # Display - display_driver = 'blender' - dspy_chan_Ci = self.rman.SGManager.RixSGDisplayChannel('color', 'Ci') - dspy_chan_a = self.rman.SGManager.RixSGDisplayChannel('float', 'a') - - self.sg_scene.SetDisplayChannel([dspy_chan_Ci, dspy_chan_a]) - display = self.rman.SGManager.RixSGShader("Display", display_driver, 'blender_preview') - display.params.SetString("mode", 'Ci,a') - self.main_camera.sg_camera_node.SetDisplay(display) - - rfb_log().debug("Calling materials()") - self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) - rfb_log().debug("Calling export_data_blocks()") - - self.export_data_blocks() - - def export_root_sg_node(self): - - rm = self.bl_scene.renderman - root_sg = self.get_root_sg_node() - attrs = root_sg.GetAttributes() - - # set any properties marked riattr in the config file - for prop_name, meta in rm.prop_meta.items(): - property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False, remove=False) - - if rm.invert_light_linking: - all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)] - all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(self.bl_scene)] - for ll in rm.light_links: - light_ob = ll.light_ob - light_nm = string_utils.sanitize_node_name(light_ob.name) - light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) - if light_props.renderman_light_role == 'RMAN_LIGHT': - if light_nm in all_lights: - all_lights.remove(light_nm) - elif light_nm in all_lightfilters: - all_lightfilters.remove(light_nm) - - if all_lights: - attrs.SetString(self.rman.Tokens.Rix.k_lighting_subset, ' '. join(all_lights) ) - else: - attrs.SetString(self.rman.Tokens.Rix.k_lighting_subset, '*') - - if all_lightfilters: - attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, ' '. join(all_lightfilters) ) - else: - attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, '*') - - root_sg.SetAttributes(attrs) - - def get_root_sg_node(self): - return self.sg_scene.Root() - - def export_materials(self, materials): - for mat in materials: - db_name = object_utils.get_db_name(mat) - rman_sg_material = self.rman_translators['MATERIAL'].export(mat.original, db_name) - if rman_sg_material: - self.rman_materials[mat.original] = rman_sg_material - - def check_visibility(self, instance): - if not self.is_interactive: - return True - viewport = self.context.space_data - if viewport is None or viewport.type != 'VIEW_3D': - return True - - if instance.is_instance: - ob_eval = instance.instance_object - ob_eval_visible = ob_eval.visible_in_viewport_get(viewport) - parent_visible = instance.parent.visible_in_viewport_get(viewport) - return (ob_eval_visible or parent_visible) - - ob_eval = instance.object.evaluated_get(self.depsgraph) - visible = ob_eval.visible_in_viewport_get(viewport) - return visible - - def is_instance_selected(self, instance): - ob = instance.object - parent = None - if instance.is_instance: - parent = instance.parent - - if not ob.original.select_get(): - if parent: - if not parent.original.select_get(): - return False - else: - return False - if parent and not parent.original.select_get(): - return False - - return True - - def get_rman_sg_instance(self, ob_inst, rman_sg_node, instance_parent, psys, create=True): - group_db_name = object_utils.get_group_db_name(ob_inst) - rman_parent_node = None - if psys and instance_parent: - rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) - if rman_parent_node: - if group_db_name in rman_parent_node.instances: - return rman_parent_node.instances[group_db_name] - else: - if group_db_name in rman_sg_node.instances: - return rman_sg_node.instances[group_db_name] - - rman_sg_group = None - if create: - rman_group_translator = self.rman_translators['GROUP'] - rman_sg_group = rman_group_translator.export(None, group_db_name) - rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) - - if rman_parent_node: - # this is an instance that comes from a particle system - # add this instance to the rman_sg_node that owns the particle system - rman_parent_node.instances[group_db_name] = rman_sg_group - else: - rman_sg_node.instances[group_db_name] = rman_sg_group - - return rman_sg_group - - def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): - rman_group_translator = self.rman_translators['GROUP'] - rman_sg_group = self.get_rman_sg_instance(ob_inst, rman_sg_node, instance_parent, psys, create=True) - is_empty_instancer = False - if instance_parent: - is_empty_instancer = object_utils.is_empty_instancer(instance_parent) - - # Object attrs - translator = self.rman_translators.get(rman_type, None) - if translator: - if rman_sg_node.shared_attrs.GetNumParams() == 0: - # export the attributes for this object - translator.export_object_attributes(ob_eval, rman_sg_group) - rman_sg_node.shared_attrs.Inherit(rman_sg_group.sg_node.GetAttributes()) - else: - # the attributes of this object have already been exported - # just call SetAttributes - rman_sg_group.sg_node.SetAttributes(rman_sg_node.shared_attrs) - - if is_empty_instancer: - translator.export_object_attributes(instance_parent, rman_sg_group, remove=False) - - translator.export_instance_attributes(ob_eval, rman_sg_group, ob_inst) - - # Add any particles necessary - if rman_sg_node.rman_sg_particle_group_node: - if (len(ob_eval.particle_systems) > 0) and ob_inst.show_particles: - rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) - - # Attach a material - if is_empty_instancer and instance_parent.renderman.rman_material_override: - self.attach_material(instance_parent, rman_sg_group) - elif psys: - self.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) - rman_sg_group.bl_psys_settings = psys.settings.original - else: - self.attach_material(ob_eval, rman_sg_group) - - if object_utils.has_empty_parent(ob_eval): - # this object is a child of an empty. Add it to the empty. - ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) - parent_proto_key = object_utils.prototype_key(ob_eval.parent) - rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) - rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform - rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) - else: - self.get_root_sg_node().AddChild(rman_sg_group.sg_node) - - if rman_type == "META": - # meta/blobbies are already in world space. Their instances don't need to - # set a transform. - return rman_sg_group - - rman_group_translator.update_transform(ob_inst, rman_sg_group) - return rman_sg_group - - - def export_data_blocks(self, selected_objects=False, objects_list=False): - total = len(self.depsgraph.object_instances) - for i, ob_inst in enumerate(self.depsgraph.object_instances): - ob = ob_inst.object - rfb_log().debug(" Exported %d/%d instances... (%s)" % (i, total, ob.name)) - self.rman_render.stats_mgr.set_export_stats("Exporting instances",i/total) - if ob.type in ('ARMATURE', 'CAMERA'): - continue - - if selected_objects and not self.is_instance_selected(ob_inst): - continue - - # only export these objects - if objects_list and ob.original not in objects_list: - continue - - if not self.check_visibility(ob_inst): - rfb_log().debug(" Object (%s) not visible" % (ob.name)) - continue - - ob_eval = ob.evaluated_get(self.depsgraph) - psys = None - instance_parent = None - proto_key = object_utils.prototype_key(ob_inst) - if ob_inst.is_instance: - psys = ob_inst.particle_system - instance_parent = ob_inst.parent - - rman_type = object_utils._detect_primitive_(ob_eval) - rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) - if not rman_sg_node: - continue - - if rman_type == 'LIGHT': - self.check_solo_light(rman_sg_node, ob_eval) - - if rman_type in object_utils._RMAN_NO_INSTANCES_: - continue - - self.export_instance(ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys) - - def export_data_block(self, proto_key, ob): - rman_type = object_utils._detect_primitive_(ob) - - if rman_type == "META": - # only add the meta instance that matches the family name - if ob.name_full != object_utils.get_meta_family(ob): - return None - - if proto_key in self.rman_prototypes: - return self.rman_prototypes[proto_key] - - translator = self.rman_translators.get(rman_type, None) - if not translator: - return None - - rman_sg_node = None - db_name = object_utils.get_db_name(ob) - rman_sg_node = translator.export(ob, db_name) - if not rman_sg_node: - return None - rman_sg_node.rman_type = rman_type - self.rman_prototypes[proto_key] = rman_sg_node - - # motion blur - # we set motion steps for this object, even if it's not moving - # it could be moving as part of a particle system - mb_segs = -1 - mb_deform_segs = -1 - if self.do_motion_blur: - mb_segs = self.bl_scene.renderman.motion_segments - mb_deform_segs = self.bl_scene.renderman.deform_motion_segments - if ob.renderman.motion_segments_override: - mb_segs = ob.renderman.motion_segments - if mb_segs > 1: - subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) - rman_sg_node.motion_steps = subframes - self.motion_steps.update(subframes) - - if ob.renderman.motion_segments_override: - mb_deform_segs = ob.renderman.deform_motion_segments - - if mb_deform_segs > 1: - subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) - rman_sg_node.deform_motion_steps = subframes - self.motion_steps.update(subframes) - - if rman_sg_node.is_transforming or rman_sg_node.is_deforming: - if mb_segs > 1 or mb_deform_segs > 1: - self.moving_objects[ob.name_full] = ob - - if mb_segs < 1: - rman_sg_node.is_transforming = False - if mb_deform_segs < 1: - rman_sg_node.is_deforming = False - - translator.update(ob, rman_sg_node) - - if len(ob.particle_systems) > 0: - # Deal with any particles now. - subframes = [] - if self.do_motion_blur: - subframes = scene_utils._get_subframes_(2, self.bl_scene) - self.motion_steps.update(subframes) - - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) - - psys_translator = self.rman_translators['PARTICLES'] - for psys in ob.particle_systems: - psys_db_name = '%s' % psys.name - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - if not rman_sg_particles: - continue - - psys_translator.set_motion_steps(rman_sg_particles, subframes) - psys_translator.update(ob, psys, rman_sg_particles) - - ob_psys = self.rman_particles.get(proto_key, dict()) - ob_psys[psys.settings.original] = rman_sg_particles - self.rman_particles[proto_key] = ob_psys - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - - if rman_type == 'EMPTY': - # If this is an empty, just export it as a coordinate system - # along with any instance attributes/materials necessary - self._export_hidden_instance(ob, rman_sg_node) - return rman_sg_node - - return rman_sg_node - - def export_instances_motion(self, selected_objects=False): - origframe = self.bl_scene.frame_current - - mb_segs = self.bl_scene.renderman.motion_segments - origframe = self.bl_scene.frame_current - - motion_steps = sorted(list(self.motion_steps)) - - first_sample = False - delta = 0.0 - if len(motion_steps) > 0: - delta = -motion_steps[0] - psys_translator = self.rman_translators['PARTICLES'] - rman_group_translator = self.rman_translators['GROUP'] - for samp, seg in enumerate(motion_steps): - first_sample = (samp == 0) - if seg < 0.0: - self.rman_render.bl_engine.frame_set(origframe - 1, subframe=1.0 + seg) - else: - self.rman_render.bl_engine.frame_set(origframe, subframe=seg) - - self.depsgraph.update() - time_samp = seg + delta # get the normlized version of the segment - total = len(self.depsgraph.object_instances) - objFound = False - - # update camera - if not first_sample and self.main_camera.is_transforming and seg in self.main_camera.motion_steps: - cam_translator = self.rman_translators['CAMERA'] - idx = 0 - for i, s in enumerate(self.main_camera.motion_steps): - if s == seg: - idx = i - break - cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) - - rfb_log().debug(" Export Sample: %i" % samp) - for i, ob_inst in enumerate(self.depsgraph.object_instances): - if selected_objects and not self.is_instance_selected(ob_inst): - continue - - if not self.check_visibility(ob_inst): - continue - - psys = None - ob = ob_inst.object.evaluated_get(self.depsgraph) - proto_key = object_utils.prototype_key(ob_inst) - rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) - self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) - instance_parent = None - rman_parent_node = None - if ob_inst.is_instance: - psys = ob_inst.particle_system - instance_parent = ob_inst.parent - rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent)) - - rman_type = object_utils._detect_primitive_(ob) - if rman_type in object_utils._RMAN_NO_INSTANCES_: - continue - - # check particles for motion - ''' - for psys in ob.particle_systems: - ob_psys = self.rman_particles.get(proto_key, None) - if not ob_psys: - continue - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if not rman_sg_particles: - continue - if not seg in rman_sg_particles.motion_steps: - continue - idx = 0 - for i, s in enumerate(rman_sg_particles.motion_steps): - if s == seg: - idx = i - break - psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) - ''' - - # object is not moving and not part of a particle system - if ob.name_full not in self.moving_objects and not psys: - continue - - rman_sg_node = self.get_rman_prototype(proto_key, ob=ob) - if not rman_sg_node: - continue - - # transformation blur - if seg in rman_sg_node.motion_steps: - idx = 0 - for i, s in enumerate(rman_sg_node.motion_steps): - if s == seg: - idx = i - break - - if rman_sg_node.is_transforming or psys: - group_db_name = object_utils.get_group_db_name(ob_inst) - if instance_parent: - rman_sg_group = rman_parent_node.instances.get(group_db_name, None) - else: - rman_sg_group = rman_sg_node.instances.get(group_db_name, None) - if rman_sg_group: - if first_sample: - rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) - rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) - - # deformation blur - if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: - rman_type = rman_sg_node.rman_type - if rman_type in ['MESH', 'FLUID']: - translator = self.rman_translators.get(rman_type, None) - if translator: - deform_idx = 0 - for i, s in enumerate(rman_sg_node.deform_motion_steps): - if s == seg: - deform_idx = i - break - translator.export_deform_sample(rman_sg_node, ob, deform_idx) - - self.rman_render.bl_engine.frame_set(origframe, subframe=0) - rfb_log().debug(" Finished exporting motion instances") - self.rman_render.stats_mgr.set_export_stats("Finished exporting motion instances", 100) - - def export_defaultlight(self): - # Export a headlight light if needed - if not self.default_light: - self.default_light = self.sg_scene.CreateAnalyticLight('__defaultlight') - sg_node = self.rman.SGManager.RixSGShader("Light", 'PxrDistantLight' , "light") - self.default_light.SetLight(sg_node) - s_orientPxrLight = [-1.0, 0.0, -0.0, 0.0, - -0.0, -1.0, -0.0, 0.0, - 0.0, 0.0, -1.0, 0.0, - 0.0, 0.0, 0.0, 1.0] - self.default_light.SetOrientTransform(s_orientPxrLight) - - if self.render_default_light and not self.scene_any_lights: - self.default_light.SetHidden(0) - else: - self.default_light.SetHidden(1) - - def _scene_has_lights(self): - # Determine if there are any lights in the scene - num_lights = len(scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)) - return num_lights > 0 - - def get_rman_prototype(self, proto_key, ob=None, create=False): - if proto_key in self.rman_prototypes: - return self.rman_prototypes[proto_key] - - if not create: - return None - - if not ob: - return None - - rman_sg_node = self.export_data_block(proto_key, ob) - return rman_sg_node - - def get_rman_particles(self, proto_key, psys, ob, create=True): - psys_translator = self.rman_translators['PARTICLES'] - group_translator = self.rman_translators['GROUP'] - ob_psys = self.rman_particles.get(proto_key, dict()) - rman_sg_particles = ob_psys.get(psys.settings.original, None) - if not rman_sg_particles and create: - psys_db_name = '%s' % psys.name - rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) - ob_psys[psys.settings.original] = rman_sg_particles - self.rman_particles[proto_key] = ob_psys - rman_sg_node = self.get_rman_prototype(proto_key) - if rman_sg_node: - if not rman_sg_node.rman_sg_particle_group_node: - particles_group_db = '' - rman_sg_node.rman_sg_particle_group_node = group_translator.export(None, particles_group_db) - rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) - return rman_sg_particles - - def _export_hidden_instance(self, ob, rman_sg_node): - translator = self.rman_translators.get('EMPTY') - translator.export_object_attributes(ob, rman_sg_node) - self.attach_material(ob, rman_sg_node) - if object_utils.has_empty_parent(ob): - parent_proto_key = object_utils.prototype_key(ob.parent) - ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) - rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) - rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) - else: - self.get_root_sg_node().AddChild(rman_sg_node.sg_node) - translator.export_transform(ob, rman_sg_node.sg_node) - if ob.renderman.export_as_coordsys: - self.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) - - def attach_material(self, ob, rman_sg_node): - mat = object_utils.get_active_material(ob) - if mat: - rman_sg_material = self.rman_materials.get(mat.original, None) - if rman_sg_material and rman_sg_material.sg_node: - scenegraph_utils.set_material(rman_sg_node.sg_node, rman_sg_material.sg_node) - rman_sg_node.is_meshlight = rman_sg_material.has_meshlight - - def attach_particle_material(self, psys_settings, parent, ob, group): - # This function should only be used by particle instancing. - # For emitters and hair, the material attachment is done in either - # the emitter translator or hair translator directly - - if not object_utils.is_particle_instancer(psys=None, particle_settings=psys_settings): - return - - if psys_settings.renderman.override_instance_material: - mat_idx = psys_settings.material - 1 - if mat_idx < len(parent.material_slots): - mat = parent.material_slots[mat_idx].material - rman_sg_material = self.rman_materials.get(mat.original, None) - if rman_sg_material: - scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) - else: - mat = object_utils.get_active_material(ob) - if mat: - rman_sg_material = self.rman_materials.get(mat.original, None) - if rman_sg_material and rman_sg_material.sg_node: - scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) - group.is_meshlight = rman_sg_material.has_meshlight - - def check_light_local_view(self, ob, rman_sg_node): - if self.is_interactive and self.context.space_data: - if not ob.visible_in_viewport_get(self.context.space_data): - rman_sg_node.sg_node.SetHidden(1) - return True - - return False - - - def check_solo_light(self, rman_sg_node, ob): - if not self.scene_solo_light: - rman_sg_node.sg_node.SetHidden(ob.renderman.mute) - else: - rm = ob.renderman - if not rm: - return - if rm.solo: - rman_sg_node.sg_node.SetHidden(0) - else: - rman_sg_node.sg_node.SetHidden(1) - - def export_searchpaths(self): - # TODO - # RMAN_ARCHIVEPATH, - # RMAN_DISPLAYPATH, RMAN_PROCEDURALPATH, and RMAN_DSOPATH (combines procedurals and displays) - - # get cycles shader directory - cycles_shader_dir = filepath_utils.get_cycles_shader_path() - - RMAN_SHADERPATH = envconfig().getenv('RMAN_SHADERPATH', '') - RMAN_TEXTUREPATH = envconfig().getenv('RMAN_TEXTUREPATH', '') - RMAN_RIXPLUGINPATH = envconfig().getenv('RMAN_RIXPLUGINPATH', '') - if sys.platform == ("win32"): - # substitute ; for : in paths - RMAN_SHADERPATH = RMAN_SHADERPATH.replace(';', ':') - RMAN_TEXTUREPATH = RMAN_TEXTUREPATH.replace(';', ':') - RMAN_RIXPLUGINPATH = RMAN_RIXPLUGINPATH.replace(';', ':') - - options = self.sg_scene.GetOptions() - options.SetString(self.rman.Tokens.Rix.k_searchpath_shader, '.:%s:%s:@' % (cycles_shader_dir, RMAN_SHADERPATH)) - options.SetString(self.rman.Tokens.Rix.k_searchpath_texture, '.:%s:@' % RMAN_TEXTUREPATH) - options.SetString(self.rman.Tokens.Rix.k_searchpath_rixplugin, '.:%s:@' % RMAN_RIXPLUGINPATH) - options.SetString(self.rman.Tokens.Rix.k_searchpath_display, '.:@') - - self.sg_scene.SetOptions(options) - - def export_hider(self): - options = self.sg_scene.GetOptions() - rm = self.bl_scene.renderman - if self.rman_bake: - options.SetString(self.rman.Tokens.Rix.k_hider_type, self.rman.Tokens.Rix.k_bake) - bakemode = rm.rman_bake_mode.lower() - primvar_s = rm.rman_bake_illum_primvarS - if primvar_s == '': - primvar_s = 's' - primvar_t = rm.rman_bake_illum_primvarT - if primvar_t == '': - primvar_t = 't' - invert_t = rm.rman_bake_illum_invertT - udim_stride = rm.rman_bake_illum_bakeudimstride - udim_offset = rm.rman_bake_illum_bakeudimoffset - options.SetString(self.rman.Tokens.Rix.k_hider_bakemode, bakemode) - options.SetStringArray(self.rman.Tokens.Rix.k_hider_primvar, (primvar_s, primvar_t), 2) - options.SetInteger(self.rman.Tokens.Rix.k_hider_invert, invert_t) - options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimstride, udim_stride) - options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimoffset, udim_offset) - else: - pv = rm.ri_pixelVariance - - options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, rm.hider_minSamples) - options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, rm.hider_maxSamples) - options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, rm.hider_incremental) - - if self.is_interactive: - options.SetInteger(self.rman.Tokens.Rix.k_hider_decidither, rm.hider_decidither) - options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, rm.ipr_hider_minSamples) - options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, rm.ipr_hider_maxSamples) - pv = rm.ipr_ri_pixelVariance - - # force incremental when checkpointing - if rm.enable_checkpoint: - options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, 1) - - if not rm.sample_motion_blur: - options.SetInteger(self.rman.Tokens.Rix.k_hider_samplemotion, 0) - - options.SetFloat(self.rman.Tokens.Rix.k_Ri_PixelVariance, pv) - - dspys_dict = display_utils.get_dspy_dict(self) - anyDenoise = False - for dspy,params in dspys_dict['displays'].items(): - if params['denoise']: - anyDenoise = True - break - if anyDenoise: - options.SetString(self.rman.Tokens.Rix.k_hider_pixelfiltermode, 'importance') - - self.sg_scene.SetOptions(options) - - def export_global_options(self): - rm = self.bl_scene.renderman - options = self.sg_scene.GetOptions() - - # set any properties marked riopt in the config file - for prop_name, meta in rm.prop_meta.items(): - property_utils.set_rioption_bl_prop(options, prop_name, meta, rm) - - # threads - if not self.external_render: - options.SetInteger(self.rman.Tokens.Rix.k_limits_threads, rm.threads) - - # pixelfilter - options.SetString(self.rman.Tokens.Rix.k_Ri_PixelFilterName, rm.ri_displayFilter) - options.SetFloatArray(self.rman.Tokens.Rix.k_Ri_PixelFilterWidth, (rm.ri_displayFilterSize[0], rm.ri_displayFilterSize[1]), 2) - - # checkpointing - if not self.is_interactive and rm.enable_checkpoint: - if rm.checkpoint_interval != '': - interval_tokens = rm.checkpoint_interval.split() - if len(interval_tokens) > 0: - options.SetStringArray(self.rman.Tokens.Rix.k_checkpoint_interval, interval_tokens, len(interval_tokens) ) - if rm.checkpoint_exitat != '': - options.SetString(self.rman.Tokens.Rix.k_checkpoint_exitat, rm.checkpoint_exitat) - - options.SetInteger(self.rman.Tokens.Rix.k_checkpoint_asfinal, int(rm.checkpoint_asfinal)) - - # Set frame number - options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.bl_scene.frame_current) - - # Always turn off xml stats when in interactive - if self.is_interactive: - options.SetInteger(self.rman.Tokens.Rix.k_statistics_level, 0) - - # Set bucket shape - bucket_order = rm.opt_bucket_order.lower() - bucket_orderorigin = [] - if rm.enable_checkpoint and not self.is_interactive: - bucket_order = 'horizontal' - - elif rm.opt_bucket_order == 'spiral': - settings = self.bl_scene.render - - if rm.opt_bucket_sprial_x <= settings.resolution_x and rm.opt_bucket_sprial_y <= settings.resolution_y: - if rm.opt_bucket_sprial_x == -1: - halfX = settings.resolution_x / 2 - bucket_orderorigin = [int(halfX), rm.opt_bucket_sprial_y] - - elif rm.opt_bucket_sprial_y == -1: - halfY = settings.resolution_y / 2 - bucket_orderorigin = [rm.opt_bucket_sprial_y, int(halfY)] - else: - bucket_orderorigin = [rm.opt_bucket_sprial_x, rm.opt_bucket_sprial_y] - - options.SetString(self.rman.Tokens.Rix.k_bucket_order, bucket_order) - if bucket_orderorigin: - options.SetFloatArray(self.rman.Tokens.Rix.k_bucket_orderorigin, bucket_orderorigin, 2) - - # Shutter - if rm.motion_blur: - shutter_interval = rm.shutter_angle / 360.0 - ''' - if rm.shutter_timing == 'FRAME_CENTER': - shutter_open, shutter_close = 0 - .5 * \ - shutter_interval, 0 + .5 * shutter_interval - elif rm.shutter_timing == 'FRAME_CLOSE': - shutter_open, shutter_close = 0 - shutter_interval, 0 - elif rm.shutter_timing == 'FRAME_OPEN': - shutter_open, shutter_close = 0, shutter_interval - ''' - shutter_open, shutter_close = 0, shutter_interval - options.SetFloatArray(self.rman.Tokens.Rix.k_Ri_Shutter, (shutter_open, shutter_close), 2) - - # dirmaps - dirmaps = '' - for k in rfb_config['dirmaps']: - dirmap = rfb_config['dirmaps'][k] - d = "[ \"%s\" \"%s\" \"%s\"]" % (dirmap['zone'], dirmap['from'], dirmap['to']) - dirmaps += d - if dirmaps: - options.SetString('searchpath:dirmap', dirmaps) - - # colorspace - ocioconfig = color_manager_blender.get_config_path() - ociocolorspacename = color_manager_blender.get_colorspace_name() - options.SetString('user:ocioconfigpath', ocioconfig) - options.SetString('user:ociocolorspacename', ociocolorspacename) - - self.sg_scene.SetOptions(options) - - def export_integrator(self): - world = self.bl_scene.world - rm = world.renderman - - bl_integrator_node = shadergraph_utils.find_integrator_node(world) - if bl_integrator_node: - integrator_sg = self.rman.SGManager.RixSGShader("Integrator", bl_integrator_node.bl_label, "integrator") - rman_sg_node = RmanSgNode(self, integrator_sg, "") - property_utils.property_group_to_rixparams(bl_integrator_node, rman_sg_node, integrator_sg, ob=world) - else: - integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrPathTracer", "integrator") - - self.sg_scene.SetIntegrator(integrator_sg) - - - def export_cameras(self, bl_cameras): - - main_cam = self.depsgraph.scene_eval.camera - cam_translator = self.rman_translators['CAMERA'] - - if self.is_viewport_render: - db_name = 'main_camera' - self.main_camera = cam_translator.export(None, db_name) - self.main_camera.sg_camera_node.SetRenderable(1) - self.sg_scene.Root().AddChild(self.main_camera.sg_node) - - # add camera so we don't mistake it for a new obj - if main_cam: - self.rman_cameras[main_cam.original] = self.main_camera - else: - if self.is_interactive: - main_cam = self.context.space_data.camera - db_name = object_utils.get_db_name(main_cam) - rman_sg_camera = cam_translator.export(main_cam, db_name) - self.main_camera = rman_sg_camera - if main_cam: - self.rman_cameras[main_cam.original] = rman_sg_camera - - # resolution - cam_translator._update_render_resolution(main_cam, self.main_camera) - - self.sg_scene.Root().AddChild(rman_sg_camera.sg_node) - - # export all other scene cameras - for cam in bl_cameras: - ob = cam.original - if cam.original in self.rman_cameras: - continue - if cam == main_cam: - if self.main_camera.is_transforming: - self.motion_steps.update(self.main_camera.motion_steps) - continue - - db_name = object_utils.get_db_name(ob) - rman_sg_camera = cam_translator._export_render_cam(ob, db_name) - - self.rman_cameras[cam.original] = rman_sg_camera - - self.sg_scene.Root().AddChild(rman_sg_camera.sg_node) - self.sg_scene.Root().AddCoordinateSystem(rman_sg_camera.sg_node) - - # For now, make the main camera the 'primary' dicing camera - self.main_camera.sg_camera_node.SetRenderable(1) - self.sg_scene.Root().AddCoordinateSystem(self.main_camera.sg_node) - - def export_displayfilters(self): - rm = self.bl_scene.renderman - display_filter_names = [] - displayfilters_list = [] - - world = self.bl_scene.world - - output = shadergraph_utils.find_node(world, 'RendermanDisplayfiltersOutputNode') - if not output: - # put in a default background color, using world color, then bail - if not self.world_df_node: - self.world_df_node = self.rman.SGManager.RixSGShader("DisplayFilter", "PxrBackgroundDisplayFilter", "__rman_world_df") - params = self.world_df_node.params - params.SetColor("backgroundColor", self.bl_scene.world.color[:3]) - self.sg_scene.SetDisplayFilter([self.world_df_node]) - return - - for bl_df_node in shadergraph_utils.find_displayfilter_nodes(world): - if not bl_df_node.is_active: - continue - - # don't emit stylized filters, if render_rman_stylized is false - if bl_df_node.bl_label in rman_constants.RMAN_STYLIZED_FILTERS and not rm.render_rman_stylized: - continue - - df_name = bl_df_node.name - - rman_df_node = self.rman.SGManager.RixSGShader("DisplayFilter", bl_df_node.bl_label, df_name) - rman_sg_node = RmanSgNode(self, rman_df_node, "") - property_utils.property_group_to_rixparams(bl_df_node, rman_sg_node, rman_df_node, ob=world) - display_filter_names.append(df_name) - displayfilters_list.append(rman_df_node) - - if len(display_filter_names) > 1: - df_name = "rman_displayfilter_combiner" - df_node = self.rman.SGManager.RixSGShader("DisplayFilter", "PxrDisplayFilterCombiner", df_name) - params = df_node.params - params.SetDisplayFilterReferenceArray("filter", display_filter_names, len(display_filter_names)) - displayfilters_list.append(df_node) - - self.sg_scene.SetDisplayFilter(displayfilters_list) - - def export_samplefilters(self, sel_chan_name=None): - rm = self.bl_scene.renderman - sample_filter_names = [] - samplefilters_list = list() - - if rm.do_holdout_matte != "OFF": - sf_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrShadowFilter", "rm_PxrShadowFilter_shadows") - params = sf_node.params - params.SetString("occludedAov", "occluded") - params.SetString("unoccludedAov", "holdoutMatte") - if rm.do_holdout_matte == "ALPHA": - params.SetString("shadowAov", "a") - else: - params.SetString("shadowAov", "holdoutMatte") - - sample_filter_names.append("rm_PxrShadowFilter_shadows") - samplefilters_list.append(sf_node) - - world = self.bl_scene.world - - for bl_sf_node in shadergraph_utils.find_samplefilter_nodes(world): - if not bl_sf_node.is_active: - continue - sf_name = bl_sf_node.name - - rman_sf_node = self.rman.SGManager.RixSGShader("SampleFilter", bl_sf_node.bl_label, sf_name) - rman_sg_node = RmanSgNode(self, rman_sf_node, "") - property_utils.property_group_to_rixparams(bl_sf_node, rman_sg_node, rman_sf_node, ob=world) - sample_filter_names.append(sf_name) - samplefilters_list.append(rman_sf_node) - - if sel_chan_name: - sf_name = '__RMAN_VIEWPORT_CHANNEL_SELECT__' - rman_sel_chan_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrCopyAOVSampleFilter", sf_name) - params = rman_sel_chan_node.params - params.SetString("readAov", sel_chan_name) - sample_filter_names.append(sf_name) - samplefilters_list.append(rman_sel_chan_node) - - - if len(sample_filter_names) > 1: - sf_name = "rman_samplefilter_combiner" - sf_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrSampleFilterCombiner", sf_name) - params = sf_node.params - params.SetSampleFilterReferenceArray("filter", sample_filter_names, len(sample_filter_names)) - - samplefilters_list.append(sf_node) - - self.sg_scene.SetSampleFilter(samplefilters_list) - - def export_bake_displays(self): - rm = self.bl_scene.renderman - sg_displays = [] - displaychannels = [] - display_driver = None - cams_to_dspys = dict() - - dspys_dict = display_utils.get_dspy_dict(self) - - for chan_name, chan_params in dspys_dict['channels'].items(): - chan_type = chan_params['channelType']['value'] - chan_source = chan_params['channelSource']['value'] - chan_remap_a = chan_params['remap_a']['value'] - chan_remap_b = chan_params['remap_b']['value'] - chan_remap_c = chan_params['remap_c']['value'] - chan_exposure = chan_params['exposure']['value'] - chan_filter = chan_params['filter']['value'] - chan_filterwidth = chan_params['filterwidth']['value'] - chan_statistics = chan_params['statistics']['value'] - displaychannel = self.rman.SGManager.RixSGDisplayChannel(chan_type, chan_name) - if chan_source: - if "lpe" in chan_source: - displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s %s' % (chan_type, chan_source)) - else: - displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, chan_source) - - displaychannel.params.SetFloatArray("exposure", chan_exposure, 2) - displaychannel.params.SetFloatArray("remap", [chan_remap_a, chan_remap_b, chan_remap_c], 3) - - if chan_filter != 'default': - displaychannel.params.SetString("filter", chan_filter) - displaychannel.params.SetFloatArray("filterwidth", chan_filterwidth, 2 ) - - if chan_statistics and chan_statistics != 'none': - displaychannel.params.SetString("statistics", chan_statistics) - displaychannels.append(displaychannel) - - # baking requires we only do one channel per display. So, we create a new display - # for each channel - for dspy,dspy_params in dspys_dict['displays'].items(): - if not dspy_params['bake_mode']: - continue - display_driver = dspy_params['driverNode'] - channels = (dspy_params['params']['displayChannels']) - - if not dspy_params['bake_mode']: - # if bake is off for this aov, just render to the null display driver - dspy_file_name = dspy_params['filePath'] - display = self.rman.SGManager.RixSGShader("Display", "null", dspy_file_name) - channels = ','.join(channels) - display.params.SetString("mode", channels) - cam_dspys = cams_to_dspys.get(self.main_camera, list()) - cam_dspys.append(display) - cams_to_dspys[self.main_camera] = cam_dspys - - else: - for chan in channels: - chan_type = dspys_dict['channels'][chan]['channelType']['value'] - if chan_type != 'color': - # we can only bake color channels - continue - - dspy_file_name = dspy_params['filePath'] - if rm.rman_bake_illum_filename == 'BAKEFILEATTR': - tokens = os.path.splitext(dspy_file_name) - if tokens[1] == '': - token_dict = {'aov': dspy} - dspy_file_name = string_utils.expand_string('%s.' % dspy_file_name, - display=display_driver, - token_dict=token_dict - ) - else: - tokens = os.path.splitext(dspy_file_name) - dspy_file_name = '%s.%s%s' % (tokens[0], chan, tokens[1]) - display = self.rman.SGManager.RixSGShader("Display", display_driver, dspy_file_name) - - dspydriver_params = dspy_params['dspyDriverParams'] - if dspydriver_params: - display.params.Inherit(dspydriver_params) - display.params.SetString("mode", chan) - - if display_driver in ['deepexr', 'openexr']: - if rm.use_metadata: - display_utils.export_metadata(self.bl_scene, display.params) - - camera = dspy_params['camera'] - if camera is None: - cam_dspys = cams_to_dspys.get(self.main_camera, list()) - cam_dspys.append(display) - cams_to_dspys[self.main_camera] = cam_dspys - else: - #db_name = object_utils.get_db_name(camera) - if camera not in self.rman_cameras: - cam_dspys = cams_to_dspys.get(self.main_camera, list()) - cam_dspys.append(display) - cams_to_dspys[self.main_camera] = cam_dspys - else: - cam_sg_node = self.rman_cameras.get(camera) - cam_dspys = cams_to_dspys.get(cam_sg_node, list()) - cam_dspys.append(display) - cams_to_dspys[cam_sg_node] = cam_dspys - - for cam_sg_node,cam_dspys in cams_to_dspys.items(): - #cam = self.rman_cameras.get(db_name, None) - if not cam_sg_node: - continue - if cam_sg_node != self.main_camera: - cam_sg_node.sg_camera_node.SetRenderable(2) - cam_sg_node.sg_camera_node.SetDisplay(cam_dspys) - - self.sg_scene.SetDisplayChannel(displaychannels) - - def export_displays(self): - rm = self.bl_scene.renderman - sg_displays = [] - displaychannels = [] - display_driver = None - cams_to_dspys = dict() - - dspys_dict = display_utils.get_dspy_dict(self) - for chan_name, chan_params in dspys_dict['channels'].items(): - chan_type = chan_params['channelType']['value'] - chan_source = chan_params['channelSource']['value'] - chan_remap_a = chan_params['remap_a']['value'] - chan_remap_b = chan_params['remap_b']['value'] - chan_remap_c = chan_params['remap_c']['value'] - chan_exposure = chan_params['exposure']['value'] - chan_filter = chan_params['filter']['value'] - chan_filterwidth = chan_params['filterwidth']['value'] - chan_statistics = chan_params['statistics']['value'] - chan_shadowthreshold = chan_params['shadowthreshold']['value'] - if chan_type == 'float[2]': - chan_type = self.rman.Tokens.Rix.k_float2 - displaychannel = self.rman.SGManager.RixSGDisplayChannel(chan_type, chan_name) - if chan_source and chan_source != '': - if "lpe" in chan_source: - displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s %s' % (chan_type, chan_source)) - else: - displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s' % (chan_source)) - - displaychannel.params.SetFloatArray("exposure", chan_exposure, 2) - displaychannel.params.SetFloatArray("remap", [chan_remap_a, chan_remap_b, chan_remap_c], 3) - displaychannel.params.SetFloat("shadowthreshold", chan_shadowthreshold) - - if chan_filter != 'default': - displaychannel.params.SetString("filter", chan_filter) - displaychannel.params.SetFloatArray("filterwidth", chan_filterwidth, 2 ) - - if chan_statistics and chan_statistics != 'none': - displaychannel.params.SetString("statistics", chan_statistics) - displaychannels.append(displaychannel) - - for dspy,dspy_params in dspys_dict['displays'].items(): - display_driver = dspy_params['driverNode'] - dspy_file_name = dspy_params['filePath'] - display = self.rman.SGManager.RixSGShader("Display", display_driver, dspy_file_name) - channels = ','.join(dspy_params['params']['displayChannels']) - dspydriver_params = dspy_params['dspyDriverParams'] - if dspydriver_params: - display.params.Inherit(dspydriver_params) - display.params.SetString("mode", channels) - if display_driver == "it": - dspy_info = display_utils.make_dspy_info(self.bl_scene) - port = self.rman_render.it_port - dspy_callback = "dspyRender" - if self.is_interactive: - dspy_callback = "dspyIPR" - display.params.SetString("dspyParams", - "%s -port %d -crop 1 0 1 0 -notes %s" % (dspy_callback, port, dspy_info)) - - cam_sg_node = self.main_camera - camera = dspy_params['camera'] - if camera and camera in self.rman_cameras: - cam_sg_node = self.rman_cameras.get(camera) - - if display_driver in ['deepexr', 'openexr']: - if rm.use_metadata: - display_utils.export_metadata(self.bl_scene, display.params, camera_name=cam_sg_node.db_name) - if not dspy_params['denoise']: - display.params.SetInteger("asrgba", 1) - - cam_dspys = cams_to_dspys.get(cam_sg_node, list()) - cam_dspys.append(display) - cams_to_dspys[cam_sg_node] = cam_dspys - - for cam_sg_node,cam_dspys in cams_to_dspys.items(): - #cam = self.rman_cameras.get(db_name, None) - if not cam_sg_node: - continue - if cam_sg_node != self.main_camera: - cam_sg_node.sg_camera_node.SetRenderable(2) - cam_sg_node.sg_camera_node.SetDisplay(cam_dspys) - - self.sg_scene.SetDisplayChannel(displaychannels) - - def export_stats(self): - - stats_mgr = self.rman_render.stats_mgr - rm = self.bl_scene.renderman - - integrator = 'PxrPathTracer' - world = self.bl_scene.world - - bl_integrator_node = shadergraph_utils.find_integrator_node(world) - if bl_integrator_node: - integrator = bl_integrator_node.bl_label - stats_mgr._integrator = integrator - #stats_mgr._minSamples = rm.hider_minSamples - stats_mgr._maxSamples = rm.hider_maxSamples - - def export_viewport_stats(self, integrator=''): - - stats_mgr = self.rman_render.stats_mgr - rm = self.bl_scene.renderman - if integrator == '': - integrator = 'PxrPathTracer' - world = self.bl_scene.world - - bl_integrator_node = shadergraph_utils.find_integrator_node(world) - if bl_integrator_node: - integrator = bl_integrator_node.bl_label - stats_mgr._integrator = integrator - #stats_mgr._minSamples = rm.ipr_hider_minSamples - stats_mgr._maxSamples = rm.ipr_hider_maxSamples - stats_mgr._decidither = rm.hider_decidither - stats_mgr._res_mult = int(self.viewport_render_res_mult*100) +# Translators +from .rman_translators.rman_camera_translator import RmanCameraTranslator +from .rman_translators.rman_light_translator import RmanLightTranslator +from .rman_translators.rman_lightfilter_translator import RmanLightFilterTranslator +from .rman_translators.rman_mesh_translator import RmanMeshTranslator +from .rman_translators.rman_material_translator import RmanMaterialTranslator +from .rman_translators.rman_hair_translator import RmanHairTranslator +from .rman_translators.rman_group_translator import RmanGroupTranslator +from .rman_translators.rman_points_translator import RmanPointsTranslator +from .rman_translators.rman_quadric_translator import RmanQuadricTranslator +from .rman_translators.rman_blobby_translator import RmanBlobbyTranslator +from .rman_translators.rman_particles_translator import RmanParticlesTranslator +from .rman_translators.rman_procedural_translator import RmanProceduralTranslator +from .rman_translators.rman_dra_translator import RmanDraTranslator +from .rman_translators.rman_runprogram_translator import RmanRunProgramTranslator +from .rman_translators.rman_openvdb_translator import RmanOpenVDBTranslator +from .rman_translators.rman_gpencil_translator import RmanGPencilTranslator +from .rman_translators.rman_fluid_translator import RmanFluidTranslator +from .rman_translators.rman_curve_translator import RmanCurveTranslator +from .rman_translators.rman_nurbs_translator import RmanNurbsTranslator +from .rman_translators.rman_volume_translator import RmanVolumeTranslator +from .rman_translators.rman_brickmap_translator import RmanBrickmapTranslator +from .rman_translators.rman_emitter_translator import RmanEmitterTranslator +from .rman_translators.rman_empty_translator import RmanEmptyTranslator +from .rman_translators.rman_alembic_translator import RmanAlembicTranslator + +# utils +from .rfb_utils import object_utils +from .rfb_utils import transform_utils +from .rfb_utils import property_utils +from .rfb_utils import display_utils +from .rfb_utils import string_utils +from .rfb_utils import texture_utils +from .rfb_utils import filepath_utils +from .rfb_utils.envconfig_utils import envconfig +from .rfb_utils import scene_utils +from .rfb_utils.prefs_utils import get_pref +from .rfb_utils import shadergraph_utils +from .rfb_utils import color_manager_blender +from .rfb_utils import scenegraph_utils + +# config +from .rman_config import __RFB_CONFIG_DICT__ as rfb_config +from . import rman_constants + +from .rfb_logger import rfb_log +from .rman_sg_nodes.rman_sg_node import RmanSgNode + +import bpy +import os +import sys + +class RmanScene(object): + ''' + The RmanScene handles translating the Blender scene. + + Attributes: + rman_render (RmanRender) - pointer back to the current RmanRender object + rman () - rman python module + sg_scene (RixSGSCene) - the RenderMan scene graph object + context (bpy.types.Context) - the current Blender context object + depsgraph (bpy.types.Depsgraph) - the Blender dependency graph + bl_scene (bpy.types.Scene) - the current Blender scene object + bl_frame_current (int) - the current Blender frame + bl_view_layer (bpy.types.ViewLayer) - the current Blender view layer + rm_rl (RendermanRenderLayerSettings) - the current rman layer + do_motion_blur (bool) - user requested for motion blur + rman_bake (bool) - user requested a bake render + is_interactive (bool) - whether we are in interactive mode + external_render (bool) - whether we are exporting for external (RIB) renders + is_viewport_render (bool) - whether we are rendering into Blender's viewport + scene_solo_light (bool) - user has solo'd a light (all other lights are muted) + rman_materials (dict) - dictionary of scene's materials + rman_translators (dict) - dictionary of all RmanTranslator(s) + rman_particles (dict) - dictionary of all particle systems used + rman_cameras (dict) - dictionary of all cameras in the scene + obj_hash (dict) - dictionary of hashes to objects ( for object picking ) + moving_objects (dict) - dictionary of objects that are moving/deforming in the scene + motion_steps (set) - the full set of motion steps for the scene, including + overrides from individual objects + main_camera (RmanSgCamera) - pointer to the main scene camera + rman_root_sg_node (RixSGGroup) - the main root RixSceneGraph node + render_default_light (bool) - whether to add a "headlight" light when there are no lights in the scene + world_df_node (RixSGShader) - a display filter shader that represents the world color + default_light (RixSGAnalyticLight) - the default "headlight" light + viewport_render_res_mult (float) - the current render resolution multiplier (for IPR) + num_object_instances (int) - the current number of object instances. This is used during IPR to + track the number of instances between edits. We try to use this to determine + when an object is added or deleted. + num_objects_in_viewlayer (int) - the current number of objects in the current view layer. We're using this + to keep track if an object was removed from a collection + objects_in_viewlayer (list) - the list of objects (bpy.types.Object) in this view layer. + ''' + + def __init__(self, rman_render=None): + self.rman_render = rman_render + self.rman = rman_render.rman + self.sg_scene = None + self.context = None + self.depsgraph = None + self.bl_scene = None + self.bl_frame_current = None + self.bl_view_layer = None + self.rm_rl = None + + self.do_motion_blur = False + self.rman_bake = False + self.is_interactive = False + self.external_render = False + self.is_viewport_render = False + self.is_swatch_render = False + self.scene_solo_light = False + self.scene_any_lights = False + self.is_xpu = False + + self.rman_materials = dict() + self.rman_translators = dict() + self.rman_particles = dict() + self.rman_cameras = dict() + self.obj_hash = dict() + self.moving_objects = dict() + self.rman_prototypes = dict() + + self.motion_steps = set() + self.main_camera = None + self.rman_root_sg_node = None + + self.render_default_light = False + self.world_df_node = None + self.default_light = None + + self.viewport_render_res_mult = 1.0 + self.num_object_instances = 0 + self.num_objects_in_viewlayer = 0 + self.objects_in_viewlayer = list() + + self.ipr_render_into = 'blender' + + self.create_translators() + + + def create_translators(self): + # Create our dictionary of translators. The object type is determined + # by the "_detect_primitive_" function in rfb_utils/object_utils.py + + self.rman_translators['CAMERA'] = RmanCameraTranslator(rman_scene=self) + self.rman_translators['LIGHT'] = RmanLightTranslator(rman_scene=self) + self.rman_translators['LIGHTFILTER'] = RmanLightFilterTranslator(rman_scene=self) + self.rman_translators['MATERIAL'] = RmanMaterialTranslator(rman_scene=self) + self.rman_translators['HAIR'] = RmanHairTranslator(rman_scene=self) + self.rman_translators['GROUP'] = RmanGroupTranslator(rman_scene=self) + self.rman_translators['EMPTY'] = RmanEmptyTranslator(rman_scene=self) + self.rman_translators['EMPTY_INSTANCER'] = RmanEmptyTranslator(rman_scene=self) + self.rman_translators['POINTS'] = RmanPointsTranslator(rman_scene=self) + self.rman_translators['META'] = RmanBlobbyTranslator(rman_scene=self) + self.rman_translators['PARTICLES'] = RmanParticlesTranslator(rman_scene=self) + self.rman_translators['EMITTER'] = RmanEmitterTranslator(rman_scene=self) + self.rman_translators['DYNAMIC_LOAD_DSO'] = RmanProceduralTranslator(rman_scene=self) + self.rman_translators['DELAYED_LOAD_ARCHIVE'] = RmanDraTranslator(rman_scene=self) + self.rman_translators['PROCEDURAL_RUN_PROGRAM'] = RmanRunProgramTranslator(rman_scene=self) + self.rman_translators['OPENVDB'] = RmanOpenVDBTranslator(rman_scene=self) + self.rman_translators['GPENCIL'] = RmanGPencilTranslator(rman_scene=self) + self.rman_translators['MESH'] = RmanMeshTranslator(rman_scene=self) + self.rman_translators['QUADRIC'] = RmanQuadricTranslator(rman_scene=self) + self.rman_translators['FLUID'] = RmanFluidTranslator(rman_scene=self) + self.rman_translators['CURVE'] = RmanCurveTranslator(rman_scene=self) + self.rman_translators['NURBS'] = RmanNurbsTranslator(rman_scene=self) + self.rman_translators['RI_VOLUME'] = RmanVolumeTranslator(rman_scene=self) + self.rman_translators['BRICKMAP'] = RmanBrickmapTranslator(rman_scene=self) + self.rman_translators['ALEMBIC'] = RmanAlembicTranslator(rman_scene=self) + + def _find_renderman_layer(self): + self.rm_rl = None + if self.bl_view_layer.renderman.use_renderman: + self.rm_rl = self.bl_view_layer.renderman + + def reset(self): + # clear out dictionaries etc. + self.rman_materials.clear() + self.rman_particles.clear() + self.rman_cameras.clear() + self.obj_hash.clear() + self.motion_steps = set() + self.moving_objects.clear() + self.rman_prototypes.clear() + + self.main_camera = None + self.render_default_light = False + self.world_df_node = None + self.default_light = None + self.is_xpu = False + self.num_object_instances = 0 + self.num_objects_in_viewlayer = 0 + self.objects_in_viewlayer.clear() + + try: + if self.is_viewport_render: + self.viewport_render_res_mult = float(self.context.scene.renderman.viewport_render_res_mult) + else: + self.viewport_render_res_mult = 1.0 + except AttributeError as err: + rfb_log().debug("Cannot set viewport_render_res_mult: %s" % str(err)) + + + def export_for_final_render(self, depsgraph, sg_scene, bl_view_layer, is_external=False): + self.sg_scene = sg_scene + self.context = bpy.context + self.bl_scene = depsgraph.scene_eval + self.bl_view_layer = bl_view_layer + self._find_renderman_layer() + self.depsgraph = depsgraph + self.external_render = is_external + self.is_interactive = False + self.is_viewport_render = False + self.do_motion_blur = self.bl_scene.renderman.motion_blur + self.export() + + def export_for_bake_render(self, depsgraph, sg_scene, bl_view_layer, is_external=False): + self.sg_scene = sg_scene + self.context = bpy.context + self.bl_scene = depsgraph.scene_eval + self.bl_view_layer = bl_view_layer + self._find_renderman_layer() + self.depsgraph = depsgraph + self.external_render = is_external + self.is_interactive = False + self.is_viewport_render = False + self.do_motion_blur = self.bl_scene.renderman.motion_blur + self.rman_bake = True + + if self.bl_scene.renderman.hider_type == 'BAKE_BRICKMAP_SELECTED': + self.export_bake_brickmap_selected() + else: + self.export_bake_render_scene() + + def export_for_interactive_render(self, context, depsgraph, sg_scene): + self.sg_scene = sg_scene + self.context = context + self.bl_view_layer = context.view_layer + self.bl_scene = depsgraph.scene_eval + self._find_renderman_layer() + self.depsgraph = depsgraph + self.external_render = False + self.is_interactive = True + self.is_viewport_render = False + self.rman_bake = False + + if self.ipr_render_into == 'blender': + self.is_viewport_render = True + + self.do_motion_blur = False + + self.export() + + def export_for_rib_selection(self, context, sg_scene): + self.reset() + self.bl_scene = context.scene + self.bl_frame_current = self.bl_scene.frame_current + self.sg_scene = sg_scene + self.context = context + self.bl_view_layer = context.view_layer + self._find_renderman_layer() + self.rman_bake = False + self.external_render = False + self.is_interactive = False + self.is_viewport_render = False + + self.depsgraph = context.evaluated_depsgraph_get() + self.export_root_sg_node() + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + self.export_data_blocks(selected_objects=True) + + def export_for_swatch_render(self, depsgraph, sg_scene): + self.sg_scene = sg_scene + self.context = bpy.context #None + self.bl_scene = depsgraph.scene_eval + self.depsgraph = depsgraph + self.external_render = False + self.is_interactive = False + self.is_viewport_render = False + self.do_motion_blur = False + self.rman_bake = False + self.is_swatch_render = True + self.export_swatch_render_scene() + + def export(self): + + self.reset() + + self.render_default_light = self.bl_scene.renderman.render_default_light + if sys.platform != "darwin": + self.is_xpu = (self.bl_scene.renderman.renderVariant != 'prman') + + # update variables + string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) + string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) + + self.bl_frame_current = self.bl_scene.frame_current + string_utils.update_frame_token(self.bl_frame_current) + + rfb_log().debug("Creating root scene graph node") + self.export_root_sg_node() + + rfb_log().debug("Calling export_materials()") + #self.export_materials(bpy.data.materials) + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + + # tell the texture manager to start converting any unconverted textures + # normally textures are converted as they are added to the scene + rfb_log().debug("Calling txmake_all()") + texture_utils.get_txmanager().rman_scene = self + texture_utils.get_txmanager().txmake_all(blocking=True) + + self.scene_any_lights = self._scene_has_lights() + + rfb_log().debug("Calling export_data_blocks()") + self.export_data_blocks() + + self.export_searchpaths() + self.export_global_options() + self.export_hider() + self.export_integrator() + + self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) + + # export default light + self.export_defaultlight() + self.main_camera.sg_node.AddChild(self.default_light) + + self.export_displays() + self.export_samplefilters() + self.export_displayfilters() + + if self.do_motion_blur: + rfb_log().debug("Calling export_instances_motion()") + self.export_instances_motion() + + self.rman_render.stats_mgr.set_export_stats("Finished Export", 1.0) + self.num_object_instances = len(self.depsgraph.object_instances) + visible_objects = getattr(self.context, 'visible_objects', list()) + self.num_objects_in_viewlayer = len(visible_objects) + self.objects_in_viewlayer = [o for o in visible_objects] + + if self.is_interactive: + self.export_viewport_stats() + else: + self.export_stats() + + def export_bake_render_scene(self): + self.reset() + + # update tokens + string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) + string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) + + self.bl_frame_current = self.bl_scene.frame_current + rfb_log().debug("Creating root scene graph node") + self.export_root_sg_node() + + rfb_log().debug("Calling export_materials()") + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + + rfb_log().debug("Calling txmake_all()") + texture_utils.get_txmanager().rman_scene = self + texture_utils.get_txmanager().txmake_all(blocking=True) + + self.scene_any_lights = self._scene_has_lights() + + rm = self.bl_scene.renderman + rman_root_sg_node = self.get_root_sg_node() + attrs = rman_root_sg_node.GetAttributes() + attrs.SetFloat("dice:worlddistancelength", rm.rman_bake_illlum_density) + rman_root_sg_node.SetAttributes(attrs) + + rfb_log().debug("Calling export_data_blocks()") + self.export_data_blocks() + + self.export_searchpaths() + self.export_global_options() + self.export_hider() + self.export_integrator() + self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) + + # export default light + self.export_defaultlight() + self.main_camera.sg_node.AddChild(self.default_light) + + self.export_bake_displays() + self.export_samplefilters() + self.export_displayfilters() + + if self.do_motion_blur: + rfb_log().debug("Calling export_instances_motion()") + self.export_instances_motion() + + options = self.sg_scene.GetOptions() + bake_resolution = int(rm.rman_bake_illlum_res) + options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (bake_resolution, bake_resolution), 2) + self.sg_scene.SetOptions(options) + + def export_bake_brickmap_selected(self): + self.reset() + + # update variables + string_utils.set_var('scene', self.bl_scene.name.replace(' ', '_')) + string_utils.set_var('layer', self.bl_view_layer.name.replace(' ', '_')) + + self.bl_frame_current = self.bl_scene.frame_current + rfb_log().debug("Creating root scene graph node") + self.export_root_sg_node() + + rfb_log().debug("Calling export_materials()") + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + rfb_log().debug("Calling txmake_all()") + texture_utils.get_txmanager().rman_scene = self + texture_utils.get_txmanager().txmake_all(blocking=True) + + self.scene_any_lights = self._scene_has_lights() + + rm = self.bl_scene.renderman + rman_root_sg_node = self.get_root_sg_node() + attrs = rman_root_sg_node.GetAttributes() + attrs.SetFloat("dice:worlddistancelength", rm.rman_bake_illlum_density) + rman_root_sg_node.SetAttributes(attrs) + + self.export_searchpaths() + self.export_global_options() + self.export_hider() + self.export_integrator() + self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) + + # export default light + self.export_defaultlight() + self.main_camera.sg_node.AddChild(self.default_light) + + ob = self.context.active_object + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + objects_needed = [x.original for x in self.bl_scene.objects if object_utils._detect_primitive_(x) == 'LIGHT'] + objects_needed.append(ob.original) + self.export_data_blocks(objects_list=objects_needed) + + self.export_samplefilters() + self.export_displayfilters() + + options = self.sg_scene.GetOptions() + bake_resolution = int(rm.rman_bake_illlum_res) + options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (bake_resolution, bake_resolution), 2) + self.sg_scene.SetOptions(options) + + # Display + display_driver = 'pointcloud' + dspy_chan_Ci = self.rman.SGManager.RixSGDisplayChannel('color', 'Ci') + + self.sg_scene.SetDisplayChannel([dspy_chan_Ci]) + render_output = '%s.ptc' % ob.renderman.bake_filename_attr + render_output = string_utils.expand_string(render_output) + display = self.rman.SGManager.RixSGShader("Display", display_driver, render_output) + display.params.SetString("mode", 'Ci') + self.main_camera.sg_camera_node.SetDisplay(display) + + def export_swatch_render_scene(self): + self.reset() + + # options + options = self.sg_scene.GetOptions() + options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, get_pref('rman_preview_renders_minSamples', default=0)) + options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, get_pref('rman_preview_renders_minSamples', default=1)) + options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, 1) + options.SetString("adaptivemetric", "variance") + scale = 100.0 / self.bl_scene.render.resolution_percentage + w = int(self.bl_scene.render.resolution_x * scale) + h = int(self.bl_scene.render.resolution_y * scale) + options.SetIntegerArray(self.rman.Tokens.Rix.k_Ri_FormatResolution, (w, h), 2) + options.SetFloat(self.rman.Tokens.Rix.k_Ri_PixelVariance, get_pref('rman_preview_renders_pixelVariance', default=0.15)) + options.SetInteger(self.rman.Tokens.Rix.k_limits_threads, -2) + options.SetString(self.rman.Tokens.Rix.k_bucket_order, 'horizontal') + self.sg_scene.SetOptions(options) + + # searchpaths + self.export_searchpaths() + + # integrator + integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrDirectLighting", "integrator") + self.sg_scene.SetIntegrator(integrator_sg) + + # camera + self.export_cameras([c for c in self.depsgraph.objects if isinstance(c.data, bpy.types.Camera)]) + + # Display + display_driver = 'blender' + dspy_chan_Ci = self.rman.SGManager.RixSGDisplayChannel('color', 'Ci') + dspy_chan_a = self.rman.SGManager.RixSGDisplayChannel('float', 'a') + + self.sg_scene.SetDisplayChannel([dspy_chan_Ci, dspy_chan_a]) + display = self.rman.SGManager.RixSGShader("Display", display_driver, 'blender_preview') + display.params.SetString("mode", 'Ci,a') + self.main_camera.sg_camera_node.SetDisplay(display) + + rfb_log().debug("Calling materials()") + self.export_materials([m for m in self.depsgraph.ids if isinstance(m, bpy.types.Material)]) + rfb_log().debug("Calling export_data_blocks()") + + self.export_data_blocks() + + def export_root_sg_node(self): + + rm = self.bl_scene.renderman + root_sg = self.get_root_sg_node() + attrs = root_sg.GetAttributes() + + # set any properties marked riattr in the config file + for prop_name, meta in rm.prop_meta.items(): + property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False, remove=False) + + if rm.invert_light_linking: + all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)] + all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(self.bl_scene)] + for ll in rm.light_links: + light_ob = ll.light_ob + light_nm = string_utils.sanitize_node_name(light_ob.name) + light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) + if light_props.renderman_light_role == 'RMAN_LIGHT': + if light_nm in all_lights: + all_lights.remove(light_nm) + elif light_nm in all_lightfilters: + all_lightfilters.remove(light_nm) + + if all_lights: + attrs.SetString(self.rman.Tokens.Rix.k_lighting_subset, ' '. join(all_lights) ) + else: + attrs.SetString(self.rman.Tokens.Rix.k_lighting_subset, '*') + + if all_lightfilters: + attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, ' '. join(all_lightfilters) ) + else: + attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, '*') + + root_sg.SetAttributes(attrs) + + def get_root_sg_node(self): + return self.sg_scene.Root() + + def export_materials(self, materials): + for mat in materials: + db_name = object_utils.get_db_name(mat) + rman_sg_material = self.rman_translators['MATERIAL'].export(mat.original, db_name) + if rman_sg_material: + self.rman_materials[mat.original] = rman_sg_material + + def check_visibility(self, instance): + if not self.is_interactive: + return True + viewport = self.context.space_data + if viewport is None or viewport.type != 'VIEW_3D': + return True + + if instance.is_instance: + ob_eval = instance.instance_object + ob_eval_visible = ob_eval.visible_in_viewport_get(viewport) + parent_visible = instance.parent.visible_in_viewport_get(viewport) + return (ob_eval_visible or parent_visible) + + ob_eval = instance.object.evaluated_get(self.depsgraph) + visible = ob_eval.visible_in_viewport_get(viewport) + return visible + + def is_instance_selected(self, instance): + ob = instance.object + parent = None + if instance.is_instance: + parent = instance.parent + + if not ob.original.select_get(): + if parent: + if not parent.original.select_get(): + return False + else: + return False + if parent and not parent.original.select_get(): + return False + + return True + + def get_rman_sg_instance(self, ob_inst, rman_sg_node, instance_parent, psys, create=True): + group_db_name = object_utils.get_group_db_name(ob_inst) + rman_parent_node = None + if psys and instance_parent: + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent), ob=instance_parent, create=True) + if rman_parent_node: + if group_db_name in rman_parent_node.instances: + return rman_parent_node.instances[group_db_name] + else: + if group_db_name in rman_sg_node.instances: + return rman_sg_node.instances[group_db_name] + + rman_sg_group = None + if create: + rman_group_translator = self.rman_translators['GROUP'] + rman_sg_group = rman_group_translator.export(None, group_db_name) + rman_sg_group.sg_node.AddChild(rman_sg_node.sg_node) + + if rman_parent_node: + # this is an instance that comes from a particle system + # add this instance to the rman_sg_node that owns the particle system + rman_parent_node.instances[group_db_name] = rman_sg_group + else: + rman_sg_node.instances[group_db_name] = rman_sg_group + + return rman_sg_group + + def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys): + rman_group_translator = self.rman_translators['GROUP'] + rman_sg_group = self.get_rman_sg_instance(ob_inst, rman_sg_node, instance_parent, psys, create=True) + is_empty_instancer = False + if instance_parent: + is_empty_instancer = object_utils.is_empty_instancer(instance_parent) + + # Object attrs + translator = self.rman_translators.get(rman_type, None) + if translator: + if rman_sg_node.shared_attrs.GetNumParams() == 0: + # export the attributes for this object + translator.export_object_attributes(ob_eval, rman_sg_group) + rman_sg_node.shared_attrs.Inherit(rman_sg_group.sg_node.GetAttributes()) + else: + # the attributes of this object have already been exported + # just call SetAttributes + rman_sg_group.sg_node.SetAttributes(rman_sg_node.shared_attrs) + + if is_empty_instancer: + translator.export_object_attributes(instance_parent, rman_sg_group, remove=False) + + translator.export_instance_attributes(ob_eval, rman_sg_group, ob_inst) + + # Add any particles necessary + if rman_sg_node.rman_sg_particle_group_node: + if (len(ob_eval.particle_systems) > 0) and ob_inst.show_particles: + rman_sg_group.sg_node.AddChild(rman_sg_node.rman_sg_particle_group_node.sg_node) + + # Attach a material + if is_empty_instancer and instance_parent.renderman.rman_material_override: + self.attach_material(instance_parent, rman_sg_group) + elif psys: + self.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) + rman_sg_group.bl_psys_settings = psys.settings.original + else: + self.attach_material(ob_eval, rman_sg_group) + + if object_utils.has_empty_parent(ob_eval): + # this object is a child of an empty. Add it to the empty. + ob_parent_eval = ob_eval.parent.evaluated_get(self.depsgraph) + parent_proto_key = object_utils.prototype_key(ob_eval.parent) + rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + else: + self.get_root_sg_node().AddChild(rman_sg_group.sg_node) + + if rman_type == "META": + # meta/blobbies are already in world space. Their instances don't need to + # set a transform. + return rman_sg_group + + rman_group_translator.update_transform(ob_inst, rman_sg_group) + return rman_sg_group + + + def export_data_blocks(self, selected_objects=False, objects_list=False): + total = len(self.depsgraph.object_instances) + for i, ob_inst in enumerate(self.depsgraph.object_instances): + ob = ob_inst.object + rfb_log().debug(" Exported %d/%d instances... (%s)" % (i, total, ob.name)) + self.rman_render.stats_mgr.set_export_stats("Exporting instances",i/total) + if ob.type in ('ARMATURE', 'CAMERA'): + continue + + if selected_objects and not self.is_instance_selected(ob_inst): + continue + + # only export these objects + if objects_list and ob.original not in objects_list: + continue + + if not self.check_visibility(ob_inst): + rfb_log().debug(" Object (%s) not visible" % (ob.name)) + continue + + ob_eval = ob.evaluated_get(self.depsgraph) + psys = None + instance_parent = None + proto_key = object_utils.prototype_key(ob_inst) + if ob_inst.is_instance: + psys = ob_inst.particle_system + instance_parent = ob_inst.parent + + rman_type = object_utils._detect_primitive_(ob_eval) + rman_sg_node = self.get_rman_prototype(proto_key, ob=ob_eval, create=True) + if not rman_sg_node: + continue + + if rman_type == 'LIGHT': + self.check_solo_light(rman_sg_node, ob_eval) + + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue + + self.export_instance(ob_eval, ob_inst, rman_sg_node, rman_type, instance_parent, psys) + + def export_data_block(self, proto_key, ob): + rman_type = object_utils._detect_primitive_(ob) + + if rman_type == "META": + # only add the meta instance that matches the family name + if ob.name_full != object_utils.get_meta_family(ob): + return None + + if proto_key in self.rman_prototypes: + return self.rman_prototypes[proto_key] + + translator = self.rman_translators.get(rman_type, None) + if not translator: + return None + + rman_sg_node = None + db_name = object_utils.get_db_name(ob) + rman_sg_node = translator.export(ob, db_name) + if not rman_sg_node: + return None + rman_sg_node.rman_type = rman_type + self.rman_prototypes[proto_key] = rman_sg_node + + # motion blur + # we set motion steps for this object, even if it's not moving + # it could be moving as part of a particle system + mb_segs = -1 + mb_deform_segs = -1 + if self.do_motion_blur: + mb_segs = self.bl_scene.renderman.motion_segments + mb_deform_segs = self.bl_scene.renderman.deform_motion_segments + if ob.renderman.motion_segments_override: + mb_segs = ob.renderman.motion_segments + if mb_segs > 1: + subframes = scene_utils._get_subframes_(mb_segs, self.bl_scene) + rman_sg_node.motion_steps = subframes + self.motion_steps.update(subframes) + + if ob.renderman.motion_segments_override: + mb_deform_segs = ob.renderman.deform_motion_segments + + if mb_deform_segs > 1: + subframes = scene_utils._get_subframes_(mb_deform_segs, self.bl_scene) + rman_sg_node.deform_motion_steps = subframes + self.motion_steps.update(subframes) + + if rman_sg_node.is_transforming or rman_sg_node.is_deforming: + if mb_segs > 1 or mb_deform_segs > 1: + self.moving_objects[ob.name_full] = ob + + if mb_segs < 1: + rman_sg_node.is_transforming = False + if mb_deform_segs < 1: + rman_sg_node.is_deforming = False + + translator.update(ob, rman_sg_node) + + if len(ob.particle_systems) > 0: + # Deal with any particles now. + subframes = [] + if self.do_motion_blur: + subframes = scene_utils._get_subframes_(2, self.bl_scene) + self.motion_steps.update(subframes) + + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = self.rman_translators['GROUP'].export(None, particles_group_db) + + psys_translator = self.rman_translators['PARTICLES'] + for psys in ob.particle_systems: + psys_db_name = '%s' % psys.name + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + if not rman_sg_particles: + continue + + psys_translator.set_motion_steps(rman_sg_particles, subframes) + psys_translator.update(ob, psys, rman_sg_particles) + + ob_psys = self.rman_particles.get(proto_key, dict()) + ob_psys[psys.settings.original] = rman_sg_particles + self.rman_particles[proto_key] = ob_psys + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + + if rman_type == 'EMPTY': + # If this is an empty, just export it as a coordinate system + # along with any instance attributes/materials necessary + self._export_hidden_instance(ob, rman_sg_node) + return rman_sg_node + + return rman_sg_node + + def export_instances_motion(self, selected_objects=False): + origframe = self.bl_scene.frame_current + + mb_segs = self.bl_scene.renderman.motion_segments + origframe = self.bl_scene.frame_current + + motion_steps = sorted(list(self.motion_steps)) + + first_sample = False + delta = 0.0 + if len(motion_steps) > 0: + delta = -motion_steps[0] + psys_translator = self.rman_translators['PARTICLES'] + rman_group_translator = self.rman_translators['GROUP'] + for samp, seg in enumerate(motion_steps): + first_sample = (samp == 0) + if seg < 0.0: + self.rman_render.bl_engine.frame_set(origframe - 1, subframe=1.0 + seg) + else: + self.rman_render.bl_engine.frame_set(origframe, subframe=seg) + + self.depsgraph.update() + time_samp = seg + delta # get the normlized version of the segment + total = len(self.depsgraph.object_instances) + objFound = False + + # update camera + if not first_sample and self.main_camera.is_transforming and seg in self.main_camera.motion_steps: + cam_translator = self.rman_translators['CAMERA'] + idx = 0 + for i, s in enumerate(self.main_camera.motion_steps): + if s == seg: + idx = i + break + cam_translator.update_transform(self.depsgraph.scene_eval.camera, self.main_camera, idx, time_samp) + + rfb_log().debug(" Export Sample: %i" % samp) + for i, ob_inst in enumerate(self.depsgraph.object_instances): + if selected_objects and not self.is_instance_selected(ob_inst): + continue + + if not self.check_visibility(ob_inst): + continue + + psys = None + ob = ob_inst.object.evaluated_get(self.depsgraph) + proto_key = object_utils.prototype_key(ob_inst) + rfb_log().debug(" Exported %d/%d motion instances... (%s)" % (i, total, ob.name)) + self.rman_render.stats_mgr.set_export_stats("Exporting motion instances (%d) " % samp ,i/total) + instance_parent = None + rman_parent_node = None + if ob_inst.is_instance: + psys = ob_inst.particle_system + instance_parent = ob_inst.parent + rman_parent_node = self.get_rman_prototype(object_utils.prototype_key(instance_parent)) + + rman_type = object_utils._detect_primitive_(ob) + if rman_type in object_utils._RMAN_NO_INSTANCES_: + continue + + # check particles for motion + ''' + for psys in ob.particle_systems: + ob_psys = self.rman_particles.get(proto_key, None) + if not ob_psys: + continue + rman_sg_particles = ob_psys.get(psys.settings.original, None) + if not rman_sg_particles: + continue + if not seg in rman_sg_particles.motion_steps: + continue + idx = 0 + for i, s in enumerate(rman_sg_particles.motion_steps): + if s == seg: + idx = i + break + psys_translator.export_deform_sample(rman_sg_particles, ob, psys, idx) + ''' + + # object is not moving and not part of a particle system + if ob.name_full not in self.moving_objects and not psys: + continue + + rman_sg_node = self.get_rman_prototype(proto_key, ob=ob) + if not rman_sg_node: + continue + + # transformation blur + if seg in rman_sg_node.motion_steps: + idx = 0 + for i, s in enumerate(rman_sg_node.motion_steps): + if s == seg: + idx = i + break + + if rman_sg_node.is_transforming or psys: + group_db_name = object_utils.get_group_db_name(ob_inst) + if instance_parent: + rman_sg_group = rman_parent_node.instances.get(group_db_name, None) + else: + rman_sg_group = rman_sg_node.instances.get(group_db_name, None) + if rman_sg_group: + if first_sample: + rman_group_translator.update_transform_num_samples(rman_sg_group, rman_sg_node.motion_steps ) + rman_group_translator.update_transform_sample( ob_inst, rman_sg_group, idx, time_samp) + + # deformation blur + if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: + rman_type = rman_sg_node.rman_type + if rman_type in ['MESH', 'FLUID']: + translator = self.rman_translators.get(rman_type, None) + if translator: + deform_idx = 0 + for i, s in enumerate(rman_sg_node.deform_motion_steps): + if s == seg: + deform_idx = i + break + translator.export_deform_sample(rman_sg_node, ob, deform_idx) + + self.rman_render.bl_engine.frame_set(origframe, subframe=0) + rfb_log().debug(" Finished exporting motion instances") + self.rman_render.stats_mgr.set_export_stats("Finished exporting motion instances", 100) + + def export_defaultlight(self): + # Export a headlight light if needed + if not self.default_light: + self.default_light = self.sg_scene.CreateAnalyticLight('__defaultlight') + sg_node = self.rman.SGManager.RixSGShader("Light", 'PxrDistantLight' , "light") + self.default_light.SetLight(sg_node) + s_orientPxrLight = [-1.0, 0.0, -0.0, 0.0, + -0.0, -1.0, -0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0] + self.default_light.SetOrientTransform(s_orientPxrLight) + + if self.render_default_light and not self.scene_any_lights: + self.default_light.SetHidden(0) + else: + self.default_light.SetHidden(1) + + def _scene_has_lights(self): + # Determine if there are any lights in the scene + num_lights = len(scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)) + return num_lights > 0 + + def get_rman_prototype(self, proto_key, ob=None, create=False): + if proto_key in self.rman_prototypes: + return self.rman_prototypes[proto_key] + + if not create: + return None + + if not ob: + return None + + rman_sg_node = self.export_data_block(proto_key, ob) + return rman_sg_node + + def get_rman_particles(self, proto_key, psys, ob, create=True): + psys_translator = self.rman_translators['PARTICLES'] + group_translator = self.rman_translators['GROUP'] + ob_psys = self.rman_particles.get(proto_key, dict()) + rman_sg_particles = ob_psys.get(psys.settings.original, None) + if not rman_sg_particles and create: + psys_db_name = '%s' % psys.name + rman_sg_particles = psys_translator.export(ob, psys, psys_db_name) + ob_psys[psys.settings.original] = rman_sg_particles + self.rman_particles[proto_key] = ob_psys + rman_sg_node = self.get_rman_prototype(proto_key) + if rman_sg_node: + if not rman_sg_node.rman_sg_particle_group_node: + particles_group_db = '' + rman_sg_node.rman_sg_particle_group_node = group_translator.export(None, particles_group_db) + rman_sg_node.rman_sg_particle_group_node.sg_node.AddChild(rman_sg_particles.sg_node) + return rman_sg_particles + + def _export_hidden_instance(self, ob, rman_sg_node): + translator = self.rman_translators.get('EMPTY') + translator.export_object_attributes(ob, rman_sg_node) + self.attach_material(ob, rman_sg_node) + if object_utils.has_empty_parent(ob): + parent_proto_key = object_utils.prototype_key(ob.parent) + ob_parent_eval = ob.parent.evaluated_get(self.depsgraph) + rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) + rman_empty_node.sg_node.AddChild(rman_sg_node.sg_node) + else: + self.get_root_sg_node().AddChild(rman_sg_node.sg_node) + translator.export_transform(ob, rman_sg_node.sg_node) + if ob.renderman.export_as_coordsys: + self.get_root_sg_node().AddCoordinateSystem(rman_sg_node.sg_node) + + def attach_material(self, ob, rman_sg_node): + mat = object_utils.get_active_material(ob) + if mat: + rman_sg_material = self.rman_materials.get(mat.original, None) + if rman_sg_material and rman_sg_material.sg_node: + scenegraph_utils.set_material(rman_sg_node.sg_node, rman_sg_material.sg_node) + rman_sg_node.is_meshlight = rman_sg_material.has_meshlight + + def attach_particle_material(self, psys_settings, parent, ob, group): + # This function should only be used by particle instancing. + # For emitters and hair, the material attachment is done in either + # the emitter translator or hair translator directly + + if not object_utils.is_particle_instancer(psys=None, particle_settings=psys_settings): + return + + if psys_settings.renderman.override_instance_material: + mat_idx = psys_settings.material - 1 + if mat_idx < len(parent.material_slots): + mat = parent.material_slots[mat_idx].material + rman_sg_material = self.rman_materials.get(mat.original, None) + if rman_sg_material: + scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) + else: + mat = object_utils.get_active_material(ob) + if mat: + rman_sg_material = self.rman_materials.get(mat.original, None) + if rman_sg_material and rman_sg_material.sg_node: + scenegraph_utils.set_material(group.sg_node, rman_sg_material.sg_node) + group.is_meshlight = rman_sg_material.has_meshlight + + def check_light_local_view(self, ob, rman_sg_node): + if self.is_interactive and self.context.space_data: + if not ob.visible_in_viewport_get(self.context.space_data): + rman_sg_node.sg_node.SetHidden(1) + return True + + return False + + + def check_solo_light(self, rman_sg_node, ob): + if not self.scene_solo_light: + rman_sg_node.sg_node.SetHidden(ob.renderman.mute) + else: + rm = ob.renderman + if not rm: + return + if rm.solo: + rman_sg_node.sg_node.SetHidden(0) + else: + rman_sg_node.sg_node.SetHidden(1) + + def export_searchpaths(self): + # TODO + # RMAN_ARCHIVEPATH, + # RMAN_DISPLAYPATH, RMAN_PROCEDURALPATH, and RMAN_DSOPATH (combines procedurals and displays) + + # get cycles shader directory + cycles_shader_dir = filepath_utils.get_cycles_shader_path() + + RMAN_SHADERPATH = envconfig().getenv('RMAN_SHADERPATH', '') + RMAN_TEXTUREPATH = envconfig().getenv('RMAN_TEXTUREPATH', '') + RMAN_RIXPLUGINPATH = envconfig().getenv('RMAN_RIXPLUGINPATH', '') + if sys.platform == ("win32"): + # substitute ; for : in paths + RMAN_SHADERPATH = RMAN_SHADERPATH.replace(';', ':') + RMAN_TEXTUREPATH = RMAN_TEXTUREPATH.replace(';', ':') + RMAN_RIXPLUGINPATH = RMAN_RIXPLUGINPATH.replace(';', ':') + + options = self.sg_scene.GetOptions() + options.SetString(self.rman.Tokens.Rix.k_searchpath_shader, '.:%s:%s:@' % (cycles_shader_dir, RMAN_SHADERPATH)) + options.SetString(self.rman.Tokens.Rix.k_searchpath_texture, '.:%s:@' % RMAN_TEXTUREPATH) + options.SetString(self.rman.Tokens.Rix.k_searchpath_rixplugin, '.:%s:@' % RMAN_RIXPLUGINPATH) + options.SetString(self.rman.Tokens.Rix.k_searchpath_display, '.:@') + + self.sg_scene.SetOptions(options) + + def export_hider(self): + options = self.sg_scene.GetOptions() + rm = self.bl_scene.renderman + if self.rman_bake: + options.SetString(self.rman.Tokens.Rix.k_hider_type, self.rman.Tokens.Rix.k_bake) + bakemode = rm.rman_bake_mode.lower() + primvar_s = rm.rman_bake_illum_primvarS + if primvar_s == '': + primvar_s = 's' + primvar_t = rm.rman_bake_illum_primvarT + if primvar_t == '': + primvar_t = 't' + invert_t = rm.rman_bake_illum_invertT + udim_stride = rm.rman_bake_illum_bakeudimstride + udim_offset = rm.rman_bake_illum_bakeudimoffset + options.SetString(self.rman.Tokens.Rix.k_hider_bakemode, bakemode) + options.SetStringArray(self.rman.Tokens.Rix.k_hider_primvar, (primvar_s, primvar_t), 2) + options.SetInteger(self.rman.Tokens.Rix.k_hider_invert, invert_t) + options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimstride, udim_stride) + options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimoffset, udim_offset) + else: + pv = rm.ri_pixelVariance + + options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, rm.hider_minSamples) + options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, rm.hider_maxSamples) + options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, rm.hider_incremental) + + if self.is_interactive: + options.SetInteger(self.rman.Tokens.Rix.k_hider_decidither, rm.hider_decidither) + options.SetInteger(self.rman.Tokens.Rix.k_hider_minsamples, rm.ipr_hider_minSamples) + options.SetInteger(self.rman.Tokens.Rix.k_hider_maxsamples, rm.ipr_hider_maxSamples) + pv = rm.ipr_ri_pixelVariance + + # force incremental when checkpointing + if rm.enable_checkpoint: + options.SetInteger(self.rman.Tokens.Rix.k_hider_incremental, 1) + + if not rm.sample_motion_blur: + options.SetInteger(self.rman.Tokens.Rix.k_hider_samplemotion, 0) + + options.SetFloat(self.rman.Tokens.Rix.k_Ri_PixelVariance, pv) + + dspys_dict = display_utils.get_dspy_dict(self) + anyDenoise = False + for dspy,params in dspys_dict['displays'].items(): + if params['denoise']: + anyDenoise = True + break + if anyDenoise: + options.SetString(self.rman.Tokens.Rix.k_hider_pixelfiltermode, 'importance') + + self.sg_scene.SetOptions(options) + + def export_global_options(self): + rm = self.bl_scene.renderman + options = self.sg_scene.GetOptions() + + # set any properties marked riopt in the config file + for prop_name, meta in rm.prop_meta.items(): + property_utils.set_rioption_bl_prop(options, prop_name, meta, rm) + + # threads + if not self.external_render: + options.SetInteger(self.rman.Tokens.Rix.k_limits_threads, rm.threads) + + # pixelfilter + options.SetString(self.rman.Tokens.Rix.k_Ri_PixelFilterName, rm.ri_displayFilter) + options.SetFloatArray(self.rman.Tokens.Rix.k_Ri_PixelFilterWidth, (rm.ri_displayFilterSize[0], rm.ri_displayFilterSize[1]), 2) + + # checkpointing + if not self.is_interactive and rm.enable_checkpoint: + if rm.checkpoint_interval != '': + interval_tokens = rm.checkpoint_interval.split() + if len(interval_tokens) > 0: + options.SetStringArray(self.rman.Tokens.Rix.k_checkpoint_interval, interval_tokens, len(interval_tokens) ) + if rm.checkpoint_exitat != '': + options.SetString(self.rman.Tokens.Rix.k_checkpoint_exitat, rm.checkpoint_exitat) + + options.SetInteger(self.rman.Tokens.Rix.k_checkpoint_asfinal, int(rm.checkpoint_asfinal)) + + # Set frame number + options.SetInteger(self.rman.Tokens.Rix.k_Ri_Frame, self.bl_scene.frame_current) + + # Always turn off xml stats when in interactive + if self.is_interactive: + options.SetInteger(self.rman.Tokens.Rix.k_statistics_level, 0) + + # Set bucket shape + bucket_order = rm.opt_bucket_order.lower() + bucket_orderorigin = [] + if rm.enable_checkpoint and not self.is_interactive: + bucket_order = 'horizontal' + + elif rm.opt_bucket_order == 'spiral': + settings = self.bl_scene.render + + if rm.opt_bucket_sprial_x <= settings.resolution_x and rm.opt_bucket_sprial_y <= settings.resolution_y: + if rm.opt_bucket_sprial_x == -1: + halfX = settings.resolution_x / 2 + bucket_orderorigin = [int(halfX), rm.opt_bucket_sprial_y] + + elif rm.opt_bucket_sprial_y == -1: + halfY = settings.resolution_y / 2 + bucket_orderorigin = [rm.opt_bucket_sprial_y, int(halfY)] + else: + bucket_orderorigin = [rm.opt_bucket_sprial_x, rm.opt_bucket_sprial_y] + + options.SetString(self.rman.Tokens.Rix.k_bucket_order, bucket_order) + if bucket_orderorigin: + options.SetFloatArray(self.rman.Tokens.Rix.k_bucket_orderorigin, bucket_orderorigin, 2) + + # Shutter + if rm.motion_blur: + shutter_interval = rm.shutter_angle / 360.0 + ''' + if rm.shutter_timing == 'FRAME_CENTER': + shutter_open, shutter_close = 0 - .5 * \ + shutter_interval, 0 + .5 * shutter_interval + elif rm.shutter_timing == 'FRAME_CLOSE': + shutter_open, shutter_close = 0 - shutter_interval, 0 + elif rm.shutter_timing == 'FRAME_OPEN': + shutter_open, shutter_close = 0, shutter_interval + ''' + shutter_open, shutter_close = 0, shutter_interval + options.SetFloatArray(self.rman.Tokens.Rix.k_Ri_Shutter, (shutter_open, shutter_close), 2) + + # dirmaps + dirmaps = '' + for k in rfb_config['dirmaps']: + dirmap = rfb_config['dirmaps'][k] + d = "[ \"%s\" \"%s\" \"%s\"]" % (dirmap['zone'], dirmap['from'], dirmap['to']) + dirmaps += d + if dirmaps: + options.SetString('searchpath:dirmap', dirmaps) + + # colorspace + ocioconfig = color_manager_blender.get_config_path() + ociocolorspacename = color_manager_blender.get_colorspace_name() + options.SetString('user:ocioconfigpath', ocioconfig) + options.SetString('user:ociocolorspacename', ociocolorspacename) + options.SetInteger('user:ocioenabled', 1 if ocioconfig else 0) + + self.sg_scene.SetOptions(options) + + def export_integrator(self): + world = self.bl_scene.world + rm = world.renderman + + bl_integrator_node = shadergraph_utils.find_integrator_node(world) + if bl_integrator_node: + integrator_sg = self.rman.SGManager.RixSGShader("Integrator", bl_integrator_node.bl_label, "integrator") + rman_sg_node = RmanSgNode(self, integrator_sg, "") + property_utils.property_group_to_rixparams(bl_integrator_node, rman_sg_node, integrator_sg, ob=world) + else: + integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrPathTracer", "integrator") + + self.sg_scene.SetIntegrator(integrator_sg) + + + def export_cameras(self, bl_cameras): + + main_cam = self.depsgraph.scene_eval.camera + cam_translator = self.rman_translators['CAMERA'] + + if self.is_viewport_render: + db_name = 'main_camera' + self.main_camera = cam_translator.export(None, db_name) + self.main_camera.sg_camera_node.SetRenderable(1) + self.sg_scene.Root().AddChild(self.main_camera.sg_node) + + # add camera so we don't mistake it for a new obj + if main_cam: + self.rman_cameras[main_cam.original] = self.main_camera + else: + if self.is_interactive: + main_cam = self.context.space_data.camera + db_name = object_utils.get_db_name(main_cam) + rman_sg_camera = cam_translator.export(main_cam, db_name) + self.main_camera = rman_sg_camera + if main_cam: + self.rman_cameras[main_cam.original] = rman_sg_camera + + # resolution + cam_translator._update_render_resolution(main_cam, self.main_camera) + + self.sg_scene.Root().AddChild(rman_sg_camera.sg_node) + + # export all other scene cameras + for cam in bl_cameras: + ob = cam.original + if cam.original in self.rman_cameras: + continue + if cam == main_cam: + if self.main_camera.is_transforming: + self.motion_steps.update(self.main_camera.motion_steps) + continue + + db_name = object_utils.get_db_name(ob) + rman_sg_camera = cam_translator._export_render_cam(ob, db_name) + + self.rman_cameras[cam.original] = rman_sg_camera + + self.sg_scene.Root().AddChild(rman_sg_camera.sg_node) + self.sg_scene.Root().AddCoordinateSystem(rman_sg_camera.sg_node) + + # For now, make the main camera the 'primary' dicing camera + self.main_camera.sg_camera_node.SetRenderable(1) + self.sg_scene.Root().AddCoordinateSystem(self.main_camera.sg_node) + + def export_displayfilters(self): + rm = self.bl_scene.renderman + display_filter_names = [] + displayfilters_list = [] + + world = self.bl_scene.world + + output = shadergraph_utils.find_node(world, 'RendermanDisplayfiltersOutputNode') + if not output: + # put in a default background color, using world color, then bail + if not self.world_df_node: + self.world_df_node = self.rman.SGManager.RixSGShader("DisplayFilter", "PxrBackgroundDisplayFilter", "__rman_world_df") + params = self.world_df_node.params + params.SetColor("backgroundColor", self.bl_scene.world.color[:3]) + self.sg_scene.SetDisplayFilter([self.world_df_node]) + return + + for bl_df_node in shadergraph_utils.find_displayfilter_nodes(world): + if not bl_df_node.is_active: + continue + + # don't emit stylized filters, if render_rman_stylized is false + if bl_df_node.bl_label in rman_constants.RMAN_STYLIZED_FILTERS and not rm.render_rman_stylized: + continue + + df_name = bl_df_node.name + + rman_df_node = self.rman.SGManager.RixSGShader("DisplayFilter", bl_df_node.bl_label, df_name) + rman_sg_node = RmanSgNode(self, rman_df_node, "") + property_utils.property_group_to_rixparams(bl_df_node, rman_sg_node, rman_df_node, ob=world) + display_filter_names.append(df_name) + displayfilters_list.append(rman_df_node) + + if len(display_filter_names) > 1: + df_name = "rman_displayfilter_combiner" + df_node = self.rman.SGManager.RixSGShader("DisplayFilter", "PxrDisplayFilterCombiner", df_name) + params = df_node.params + params.SetDisplayFilterReferenceArray("filter", display_filter_names, len(display_filter_names)) + displayfilters_list.append(df_node) + + self.sg_scene.SetDisplayFilter(displayfilters_list) + + def export_samplefilters(self, sel_chan_name=None): + rm = self.bl_scene.renderman + sample_filter_names = [] + samplefilters_list = list() + + if rm.do_holdout_matte != "OFF": + sf_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrShadowFilter", "rm_PxrShadowFilter_shadows") + params = sf_node.params + params.SetString("occludedAov", "occluded") + params.SetString("unoccludedAov", "holdoutMatte") + if rm.do_holdout_matte == "ALPHA": + params.SetString("shadowAov", "a") + else: + params.SetString("shadowAov", "holdoutMatte") + + sample_filter_names.append("rm_PxrShadowFilter_shadows") + samplefilters_list.append(sf_node) + + world = self.bl_scene.world + + for bl_sf_node in shadergraph_utils.find_samplefilter_nodes(world): + if not bl_sf_node.is_active: + continue + sf_name = bl_sf_node.name + + rman_sf_node = self.rman.SGManager.RixSGShader("SampleFilter", bl_sf_node.bl_label, sf_name) + rman_sg_node = RmanSgNode(self, rman_sf_node, "") + property_utils.property_group_to_rixparams(bl_sf_node, rman_sg_node, rman_sf_node, ob=world) + sample_filter_names.append(sf_name) + samplefilters_list.append(rman_sf_node) + + if sel_chan_name: + sf_name = '__RMAN_VIEWPORT_CHANNEL_SELECT__' + rman_sel_chan_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrCopyAOVSampleFilter", sf_name) + params = rman_sel_chan_node.params + params.SetString("readAov", sel_chan_name) + sample_filter_names.append(sf_name) + samplefilters_list.append(rman_sel_chan_node) + + + if len(sample_filter_names) > 1: + sf_name = "rman_samplefilter_combiner" + sf_node = self.rman.SGManager.RixSGShader("SampleFilter", "PxrSampleFilterCombiner", sf_name) + params = sf_node.params + params.SetSampleFilterReferenceArray("filter", sample_filter_names, len(sample_filter_names)) + + samplefilters_list.append(sf_node) + + self.sg_scene.SetSampleFilter(samplefilters_list) + + def export_bake_displays(self): + rm = self.bl_scene.renderman + sg_displays = [] + displaychannels = [] + display_driver = None + cams_to_dspys = dict() + + dspys_dict = display_utils.get_dspy_dict(self) + + for chan_name, chan_params in dspys_dict['channels'].items(): + chan_type = chan_params['channelType']['value'] + chan_source = chan_params['channelSource']['value'] + chan_remap_a = chan_params['remap_a']['value'] + chan_remap_b = chan_params['remap_b']['value'] + chan_remap_c = chan_params['remap_c']['value'] + chan_exposure = chan_params['exposure']['value'] + chan_filter = chan_params['filter']['value'] + chan_filterwidth = chan_params['filterwidth']['value'] + chan_statistics = chan_params['statistics']['value'] + displaychannel = self.rman.SGManager.RixSGDisplayChannel(chan_type, chan_name) + if chan_source: + if "lpe" in chan_source: + displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s %s' % (chan_type, chan_source)) + else: + displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, chan_source) + + displaychannel.params.SetFloatArray("exposure", chan_exposure, 2) + displaychannel.params.SetFloatArray("remap", [chan_remap_a, chan_remap_b, chan_remap_c], 3) + + if chan_filter != 'default': + displaychannel.params.SetString("filter", chan_filter) + displaychannel.params.SetFloatArray("filterwidth", chan_filterwidth, 2 ) + + if chan_statistics and chan_statistics != 'none': + displaychannel.params.SetString("statistics", chan_statistics) + displaychannels.append(displaychannel) + + # baking requires we only do one channel per display. So, we create a new display + # for each channel + for dspy,dspy_params in dspys_dict['displays'].items(): + if not dspy_params['bake_mode']: + continue + display_driver = dspy_params['driverNode'] + channels = (dspy_params['params']['displayChannels']) + + if not dspy_params['bake_mode']: + # if bake is off for this aov, just render to the null display driver + dspy_file_name = dspy_params['filePath'] + display = self.rman.SGManager.RixSGShader("Display", "null", dspy_file_name) + channels = ','.join(channels) + display.params.SetString("mode", channels) + cam_dspys = cams_to_dspys.get(self.main_camera, list()) + cam_dspys.append(display) + cams_to_dspys[self.main_camera] = cam_dspys + + else: + for chan in channels: + chan_type = dspys_dict['channels'][chan]['channelType']['value'] + if chan_type != 'color': + # we can only bake color channels + continue + + dspy_file_name = dspy_params['filePath'] + if rm.rman_bake_illum_filename == 'BAKEFILEATTR': + tokens = os.path.splitext(dspy_file_name) + if tokens[1] == '': + token_dict = {'aov': dspy} + dspy_file_name = string_utils.expand_string('%s.' % dspy_file_name, + display=display_driver, + token_dict=token_dict + ) + else: + tokens = os.path.splitext(dspy_file_name) + dspy_file_name = '%s.%s%s' % (tokens[0], chan, tokens[1]) + display = self.rman.SGManager.RixSGShader("Display", display_driver, dspy_file_name) + + dspydriver_params = dspy_params['dspyDriverParams'] + if dspydriver_params: + display.params.Inherit(dspydriver_params) + display.params.SetString("mode", chan) + + if display_driver in ['deepexr', 'openexr']: + if rm.use_metadata: + display_utils.export_metadata(self.bl_scene, display.params) + + camera = dspy_params['camera'] + if camera is None: + cam_dspys = cams_to_dspys.get(self.main_camera, list()) + cam_dspys.append(display) + cams_to_dspys[self.main_camera] = cam_dspys + else: + #db_name = object_utils.get_db_name(camera) + if camera not in self.rman_cameras: + cam_dspys = cams_to_dspys.get(self.main_camera, list()) + cam_dspys.append(display) + cams_to_dspys[self.main_camera] = cam_dspys + else: + cam_sg_node = self.rman_cameras.get(camera) + cam_dspys = cams_to_dspys.get(cam_sg_node, list()) + cam_dspys.append(display) + cams_to_dspys[cam_sg_node] = cam_dspys + + for cam_sg_node,cam_dspys in cams_to_dspys.items(): + #cam = self.rman_cameras.get(db_name, None) + if not cam_sg_node: + continue + if cam_sg_node != self.main_camera: + cam_sg_node.sg_camera_node.SetRenderable(2) + cam_sg_node.sg_camera_node.SetDisplay(cam_dspys) + + self.sg_scene.SetDisplayChannel(displaychannels) + + def export_displays(self): + rm = self.bl_scene.renderman + sg_displays = [] + displaychannels = [] + display_driver = None + cams_to_dspys = dict() + + dspys_dict = display_utils.get_dspy_dict(self) + for chan_name, chan_params in dspys_dict['channels'].items(): + chan_type = chan_params['channelType']['value'] + chan_source = chan_params['channelSource']['value'] + chan_remap_a = chan_params['remap_a']['value'] + chan_remap_b = chan_params['remap_b']['value'] + chan_remap_c = chan_params['remap_c']['value'] + chan_exposure = chan_params['exposure']['value'] + chan_filter = chan_params['filter']['value'] + chan_filterwidth = chan_params['filterwidth']['value'] + chan_statistics = chan_params['statistics']['value'] + chan_shadowthreshold = chan_params['shadowthreshold']['value'] + if chan_type == 'float[2]': + chan_type = self.rman.Tokens.Rix.k_float2 + displaychannel = self.rman.SGManager.RixSGDisplayChannel(chan_type, chan_name) + if chan_source and chan_source != '': + if "lpe" in chan_source: + displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s %s' % (chan_type, chan_source)) + else: + displaychannel.params.SetString(self.rman.Tokens.Rix.k_source, '%s' % (chan_source)) + + displaychannel.params.SetFloatArray("exposure", chan_exposure, 2) + displaychannel.params.SetFloatArray("remap", [chan_remap_a, chan_remap_b, chan_remap_c], 3) + displaychannel.params.SetFloat("shadowthreshold", chan_shadowthreshold) + + if chan_filter != 'default': + displaychannel.params.SetString("filter", chan_filter) + displaychannel.params.SetFloatArray("filterwidth", chan_filterwidth, 2 ) + + if chan_statistics and chan_statistics != 'none': + displaychannel.params.SetString("statistics", chan_statistics) + displaychannels.append(displaychannel) + + for dspy,dspy_params in dspys_dict['displays'].items(): + display_driver = dspy_params['driverNode'] + dspy_file_name = dspy_params['filePath'] + display = self.rman.SGManager.RixSGShader("Display", display_driver, dspy_file_name) + channels = ','.join(dspy_params['params']['displayChannels']) + dspydriver_params = dspy_params['dspyDriverParams'] + if dspydriver_params: + display.params.Inherit(dspydriver_params) + display.params.SetString("mode", channels) + if display_driver == "it": + dspy_info = display_utils.make_dspy_info(self.bl_scene) + port = self.rman_render.it_port + dspy_callback = "dspyRender" + if self.is_interactive: + dspy_callback = "dspyIPR" + display.params.SetString("dspyParams", + "%s -port %d -crop 1 0 1 0 -notes %s" % (dspy_callback, port, dspy_info)) + + cam_sg_node = self.main_camera + camera = dspy_params['camera'] + if camera and camera in self.rman_cameras: + cam_sg_node = self.rman_cameras.get(camera) + + if display_driver in ['deepexr', 'openexr']: + if rm.use_metadata: + display_utils.export_metadata(self.bl_scene, display.params, camera_name=cam_sg_node.db_name) + if not dspy_params['denoise']: + display.params.SetInteger("asrgba", 1) + + cam_dspys = cams_to_dspys.get(cam_sg_node, list()) + cam_dspys.append(display) + cams_to_dspys[cam_sg_node] = cam_dspys + + for cam_sg_node,cam_dspys in cams_to_dspys.items(): + #cam = self.rman_cameras.get(db_name, None) + if not cam_sg_node: + continue + if cam_sg_node != self.main_camera: + cam_sg_node.sg_camera_node.SetRenderable(2) + cam_sg_node.sg_camera_node.SetDisplay(cam_dspys) + + self.sg_scene.SetDisplayChannel(displaychannels) + + def export_stats(self): + + stats_mgr = self.rman_render.stats_mgr + rm = self.bl_scene.renderman + + integrator = 'PxrPathTracer' + world = self.bl_scene.world + + bl_integrator_node = shadergraph_utils.find_integrator_node(world) + if bl_integrator_node: + integrator = bl_integrator_node.bl_label + stats_mgr._integrator = integrator + #stats_mgr._minSamples = rm.hider_minSamples + stats_mgr._maxSamples = rm.hider_maxSamples + + def export_viewport_stats(self, integrator=''): + + stats_mgr = self.rman_render.stats_mgr + rm = self.bl_scene.renderman + if integrator == '': + integrator = 'PxrPathTracer' + world = self.bl_scene.world + + bl_integrator_node = shadergraph_utils.find_integrator_node(world) + if bl_integrator_node: + integrator = bl_integrator_node.bl_label + stats_mgr._integrator = integrator + #stats_mgr._minSamples = rm.ipr_hider_minSamples + stats_mgr._maxSamples = rm.ipr_hider_maxSamples + stats_mgr._decidither = rm.hider_decidither + stats_mgr._res_mult = int(self.viewport_render_res_mult*100) From 10bf98d4331c2d19b0af22bdcba530f99e230663 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 23 Jun 2022 13:09:45 -0700 Subject: [PATCH 161/278] Add some missing help text for bucket size and pixel filter. --- rman_config/config/rman_properties_scene.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index cb01b779..d4b597af 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -829,6 +829,7 @@ "type": "string", "default": "gaussian", "widget": "popup", + "help": "The filter for rendered pixels. The box, triangle, disk, gaussian, and blackman-harris are softening filters while catmull-rom, sinc, mitchell, separable-catmull-rom, lanczos and bessel are sharpening filters. Separable-catmull-rom is a good compromise.", "options": "box|triangle|catmull-rom|sinc|gaussian|mitchell|separable-catmull-rom|blackman-harris|lanczos|bessel|disk" }, { @@ -842,6 +843,7 @@ 2, 2 ], + "help": "The width of the pixel filter. Typically from 1 to 6.", "connectable": false }, { @@ -910,6 +912,7 @@ 16 ], "widget": "mapper", + "help": "Specify the size of the buckets. Larger buckets will require more memory to render. Conversely, smaller buckets are less efficient for shading, but will use less memory during rendering. If your scene is using a lot of memory, you may want to try setting this field down to 8 by 8 or even 4 by 4 buckets, but most likely you will not change this.", "options": "4x4:[4,4]|8x8:[8,8]|16x16:[16,16]|32x32:[32,32]|64x64:[64,64]", "connectable": false }, @@ -921,6 +924,7 @@ "type": "string", "default": "circle", "widget": "popup", + "help": "RenderMan subdivides the output image into small rectangular regions called buckets, and renders a single bucket at a time. This allows a small portion of the image to be in memory at any one time. Note that only some bucket orders are supported for checkpointing and recovery.", "options": "horizontal|vertical|zigzag-x|zigzag-y|spiral|spacefill|circle" }, { From b911039d2e78687baff3941e32ee6543868400e5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 27 Jun 2022 11:29:17 -0700 Subject: [PATCH 162/278] Fix merge conflicts with the last commit. --- rman_scene.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 20145543..86b01d9b 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -1081,9 +1081,9 @@ def export_hider(self): udim_offset = rm.rman_bake_illum_bakeudimoffset bbox_min = rm.rman_bake_illum_bakebboxmin bbox_max = rm.rman_bake_illum_bakebboxmax - + options.SetString(self.rman.Tokens.Rix.k_hider_bakemode, bakemode) - options.SetStringArray(self.rman.Tokens.Rix.k_hider_primvar, (primvar_s, primvar_t), 2) + options.SetStringArray(self.rman.Tokens.Rix.k_hider_primvar, (primvar_s, primvar_t), 2) options.SetInteger(self.rman.Tokens.Rix.k_hider_invert, invert_t) options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimstride, udim_stride) options.SetInteger(self.rman.Tokens.Rix.k_hider_bakeudimoffset, udim_offset) @@ -1595,4 +1595,4 @@ def export_viewport_stats(self, integrator=''): #stats_mgr._minSamples = rm.ipr_hider_minSamples stats_mgr._maxSamples = rm.ipr_hider_maxSamples stats_mgr._decidither = rm.hider_decidither - stats_mgr._res_mult = int(self.viewport_render_res_mult*100) + stats_mgr._res_mult = int(self.viewport_render_res_mult*100) \ No newline at end of file From 8967e66d6f2ec297d07f5f7a34900002b43e0202 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 28 Jun 2022 08:20:22 -0700 Subject: [PATCH 163/278] Fix typo in any_dspys_denoise. Was trying to reference rm_rl before it was set. --- rfb_utils/display_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index f1ca5bdb..fd756863 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -568,7 +568,8 @@ def _set_rman_holdouts_dspy_dict(dspys_dict, dspy_drv, rman_scene, expandTokens, 'params': dspy_params, 'dspyDriverParams': None} -def any_dspys_denoise(view_layer): +def any_dspys_denoise(view_layer): + rm_rl = None if view_layer.renderman.use_renderman: rm_rl = view_layer.renderman if rm_rl: From ca66a0b91cea3a7c8c8a21950ed15f5ea2dc0ed2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 7 Jul 2022 09:50:45 -0700 Subject: [PATCH 164/278] Unregister the qt version of the txmanager only if we're using Qt framework. --- rman_ui/rman_ui_texteditor.py | 1 + rman_ui/rman_ui_txmanager.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rman_ui/rman_ui_texteditor.py b/rman_ui/rman_ui_texteditor.py index 412175b8..879f8370 100644 --- a/rman_ui/rman_ui_texteditor.py +++ b/rman_ui/rman_ui_texteditor.py @@ -1,4 +1,5 @@ from ..rman_constants import RFB_ADDON_PATH +from ..rfb_logger import rfb_log import bpy import os diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index 4a2dc098..e6a392eb 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -797,8 +797,9 @@ def unregister(): rfb_log().debug('Could not unregister class: %s' % str(cls)) pass - try: - from . import rman_ui_txmanager_qt - rman_ui_txmanager_qt.unregister() - except: - pass \ No newline at end of file + if get_pref('rman_ui_framework') == 'QT': + try: + from . import rman_ui_txmanager_qt + rman_ui_txmanager_qt.unregister() + except: + pass \ No newline at end of file From dc3e15cc3c9adf71c7956ccd69c43ebc81fc8bb6 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 8 Jul 2022 10:18:03 -0700 Subject: [PATCH 165/278] Remove the hardcoded default invertT to 0. --- rfb_utils/generate_property_utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rfb_utils/generate_property_utils.py b/rfb_utils/generate_property_utils.py index 9f7d5807..1e0409ec 100644 --- a/rfb_utils/generate_property_utils.py +++ b/rfb_utils/generate_property_utils.py @@ -323,9 +323,6 @@ def generate_property(node, sp, update_function=None, set_function=None): description=param_help, set=set_function, update=update_function) else: param_default = int(param_default) if param_default else 0 - # make invertT default 0 - if param_name == 'invertT': - param_default = 0 if param_widget in ['checkbox', 'switch']: prop = BoolProperty(name=param_label, From 5cd5f60db567669e542de1fd797e70035036bcc1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 14 Jul 2022 12:01:01 -0700 Subject: [PATCH 166/278] Add conditionalVisOps for bake:activeudims and bake:udimresolutions. --- rfb_utils/property_utils.py | 1 + rman_config/config/rman_properties_object.json | 14 ++++++++++++-- rman_properties/rman_properties_object/__init__.py | 8 ++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index a33eb746..64533c0f 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -293,6 +293,7 @@ def set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=True, remove=Tr # check conditionalVisOps to see if this riattr applies # to this object expr = conditionalVisOps.get('expr', None) + node = rm if expr and not eval(expr): return diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index d8d1ec9f..31c6d5c1 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -1116,7 +1116,12 @@ "type": "string", "default": "", "widget": "default", - "help": "Active udims for Integrator baking of texture atlases. Lists and ranges of udims can be provided in the string, for example: '1001,1003,1005-1006'. The default is an empty string which means all udims get baked." + "help": "Active udims for Integrator baking of texture atlases. Lists and ranges of udims can be provided in the string, for example: '1001,1003,1005-1006'. The default is an empty string which means all udims get baked.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "rman_bake_mode", + "conditionalVisValue": "pattern" + } }, { "panel": "OBJECT_PT_renderman_object_baking", @@ -1126,7 +1131,12 @@ "type": "string", "default": "", "widget": "default", - "help": "Udim texure resolutions for Integrator baking of texture atlases. Lists and ranges of udims can be provided in the string, for example: '1001 512 512 1002,1003 64 32 1005-1006 16 16'. If the string is empty (the default) then the Display Format resolution will be used." + "help": "Udim texure resolutions for Integrator baking of texture atlases. Lists and ranges of udims can be provided in the string, for example: '1001 512 512 1002,1003 64 32 1005-1006 16 16'. If the string is empty (the default) then the Display Format resolution will be used.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "rman_bake_mode", + "conditionalVisValue": "pattern" + } } ] } \ No newline at end of file diff --git a/rman_properties/rman_properties_object/__init__.py b/rman_properties/rman_properties_object/__init__.py index 55620952..59ed2172 100644 --- a/rman_properties/rman_properties_object/__init__.py +++ b/rman_properties/rman_properties_object/__init__.py @@ -114,6 +114,14 @@ def get_object_type(self): get=get_object_type ) + def get_bake_mode(self): + scene = bpy.context.scene + return scene.renderman.rman_bake_mode + + rman_bake_mode: StringProperty( + get=get_bake_mode + ) + rman_config_classes = [ RendermanObjectSettings ] From 38fcefe09dd957cd1aa095dc08b8e48338650ab1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 14 Jul 2022 14:33:12 -0700 Subject: [PATCH 167/278] A couple of fixes for swatch renders (still not officially supported) * switch to PxrPathTracer for the integrator, so that we get a better representation for glass * buffer wasn't getting updated correctly for the display. --- rman_render.py | 14 +++++--------- rman_scene.py | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/rman_render.py b/rman_render.py index 30634578..759d5143 100644 --- a/rman_render.py +++ b/rman_render.py @@ -1113,18 +1113,14 @@ def start_swatch_render(self, depsgraph): view=render_view) layer = result.layers[0].passes.find_by_name("Combined", render_view) while not self.bl_engine.test_break() and self.rman_is_live_rendering: - time.sleep(0.001) + time.sleep(0.01) if layer: - buffer = self._get_buffer(width, height, image_num=0, as_flat=False) - if buffer: + buffer = self._get_buffer(width, height, image_num=0, num_channels=4, as_flat=False) + if buffer is None: + break + else: layer.rect = buffer self.bl_engine.update_result(result) - # try to get the buffer one last time before exiting - if layer: - buffer = self._get_buffer(width, height, image_num=0, as_flat=False) - if buffer: - layer.rect = buffer - self.bl_engine.update_result(result) self.stop_render() self.bl_engine.end_result(result) self.del_bl_engine() diff --git a/rman_scene.py b/rman_scene.py index 8099bc83..2c5b200c 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -480,7 +480,7 @@ def export_swatch_render_scene(self): self.export_searchpaths() # integrator - integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrDirectLighting", "integrator") + integrator_sg = self.rman.SGManager.RixSGShader("Integrator", "PxrPathTracer", "integrator") self.sg_scene.SetIntegrator(integrator_sg) # camera From 4a87c4d5d3845457926960e66e3164842993186b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 15 Jul 2022 08:56:46 -0700 Subject: [PATCH 168/278] If a second viewport rendering is attempted, make sure we don't call our update methods, and print an error message at the bottom of the viewport. --- rman_engine.py | 56 ++++++++++++++++++++++--------------- rman_ui/rman_ui_viewport.py | 2 ++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/rman_engine.py b/rman_engine.py index d74f3995..e88e7978 100644 --- a/rman_engine.py +++ b/rman_engine.py @@ -20,13 +20,11 @@ def __init__(self): from . import rman_render self.rman_render = rman_render.RmanRender.get_rman_render() self.export_failed = None - if self.is_preview and self.rman_render.rman_swatch_render_running: - # if a preview render is requested and a swatch render is - # already in progress, ignore this render request - return + self.ipr_already_running = False if self.rman_render.rman_interactive_running: - # If IPR is already running, just return. - # We report an error in render() if this is a render attempt + # If IPR is already running, just flag it + # and don't do anything in the update methods + self.ipr_already_running = True return def __del__(self): @@ -67,6 +65,9 @@ def view_update(self, context, depsgraph): if self.export_failed: return + if self.ipr_already_running: + return + if self.rman_render.is_ipr_to_it(): # if we are already IPRing to "it", stop the render self.rman_render.stop_render(stop_draw_thread=False) @@ -91,6 +92,10 @@ def view_draw(self, context, depsgraph): ''' if self.export_failed: return + if self.ipr_already_running: + self.draw_viewport_message(context, 'Multiple viewport rendering not supported.') + return + if self.rman_render.rman_interactive_running and not self.rman_render.rman_license_failed: self.rman_render.update_view(context, depsgraph) @@ -120,6 +125,9 @@ def update_render_passes(self, scene=None, renderlayer=None): if self.rman_render.rman_render_into != 'blender': return + if self.ipr_already_running: + return + self.rman_render.rman_scene.bl_scene = scene dspy_dict = display_utils.get_dspy_dict(self.rman_render.rman_scene, include_holdouts=False) self.register_pass(scene, renderlayer, "Combined", 4, "RGBA", 'COLOR') @@ -189,27 +197,31 @@ def render(self, depsgraph): if not for_background: self._increment_version_tokens(external_render=False) - def _draw_pixels(self, context, depsgraph): + def draw_viewport_message(self, context, msg): + w = context.region.width - scene = depsgraph.scene - w = context.region.width - h = context.region.height + pos_x = w / 2 - 100 + pos_y = 20 + blf.enable(0, blf.SHADOW) + blf.shadow_offset(0, 1, -1) + blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) + blf.size(0, 32, 36) + blf.position(0, pos_x, pos_y, 0) + blf.color(0, 1.0, 0.0, 0.0, 1.0) + blf.draw(0, "%s" % (msg)) + blf.disable(0, blf.SHADOW) + + def _draw_pixels(self, context, depsgraph): if self.rman_render.rman_license_failed: - pos_x = w / 2 - 100 - pos_y = 20 - blf.enable(0, blf.SHADOW) - blf.shadow_offset(0, 1, -1) - blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) - blf.size(0, 32, 36) - blf.position(0, pos_x, pos_y, 0) - blf.color(0, 1.0, 0.0, 0.0, 1.0) - blf.draw(0, "%s" % (self.rman_render.rman_license_failed_message)) - blf.disable(0, blf.SHADOW) - return + self.draw_viewport_message(context, self.rman_render.rman_license_failed_message) if not self.rman_render.rman_is_viewport_rendering: - return + return + + scene = depsgraph.scene + w = context.region.width + h = context.region.height # Bind shader that converts from scene linear to display space, bgl.glEnable(bgl.GL_BLEND) diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 160a3db8..aebe5466 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -800,6 +800,8 @@ def draw_rman_viewport_props(self, context): view = context.space_data rman_render = RmanRender.get_rman_render() if view.shading.type == 'RENDERED' or rman_render.is_ipr_to_it(): + if not rman_render.rman_running: + return rman_rerender_controls = rfb_icons.get_icon("rman_ipr_cancel") row.operator('renderman.stop_ipr', text="", icon_value=rman_rerender_controls.icon_id) From 9010f74398bd9dfb5ddda4d09fbf8013a1458397 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 20 Jul 2022 14:06:16 -0700 Subject: [PATCH 169/278] Fix typo. get_mesh was moved to mesh_utils. --- rman_translators/rman_fluid_translator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rman_translators/rman_fluid_translator.py b/rman_translators/rman_fluid_translator.py index ff983cb1..32e25da3 100644 --- a/rman_translators/rman_fluid_translator.py +++ b/rman_translators/rman_fluid_translator.py @@ -6,6 +6,7 @@ from ..rfb_utils import scenegraph_utils from ..rfb_utils import particles_utils from ..rfb_utils import object_utils +from ..rfb_utils import mesh_utils from ..rfb_logger import rfb_log from mathutils import Matrix import bpy @@ -113,7 +114,7 @@ def update(self, ob, rman_sg_fluid): def update_fluid_mesh(self, ob, rman_sg_fluid, psys, fluid_data): sg_node = rman_sg_fluid.rman_sg_liquid_node mesh = ob.data - (nverts, verts, P, N) = object_utils._get_mesh_(mesh, get_normals=True) + (nverts, verts, P, N) = mesh_utils.get_mesh(mesh, get_normals=True) npolys = len(nverts) npoints = len(P) numnverts = len(verts) From 20092fabe40de0ce91cfc88b95ed8daf80545055 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 20 Jul 2022 15:43:26 -0700 Subject: [PATCH 170/278] Comment out the destructor for RmanSgFluid class. For some reason, we're crashing when trying to delete dag nodes. --- rman_sg_nodes/rman_sg_fluid.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rman_sg_nodes/rman_sg_fluid.py b/rman_sg_nodes/rman_sg_fluid.py index e3ab786c..b2339fb5 100644 --- a/rman_sg_nodes/rman_sg_fluid.py +++ b/rman_sg_nodes/rman_sg_fluid.py @@ -8,7 +8,11 @@ def __init__(self, rman_scene, sg_node, db_name): self.rman_sg_liquid_node = None def __del__(self): + super().__del__() + pass + ''' if self.rman_scene.rman_render.rman_running and self.rman_scene.sg_scene: self.rman_scene.sg_scene.DeleteDagNode(self.rman_sg_volume_node) self.rman_scene.sg_scene.DeleteDagNode(self.rman_sg_liquid_node) - super().__del__() \ No newline at end of file + super().__del__() + ''' \ No newline at end of file From 93c9703909d9fb5512aa2abcd0a82ee6e82aefc2 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 21 Jul 2022 15:51:09 -0700 Subject: [PATCH 171/278] Fix some typos in the attach stylized pattern operator. --- rman_operators/rman_operators_stylized.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rman_operators/rman_operators_stylized.py b/rman_operators/rman_operators_stylized.py index 8dfad75a..00f1c615 100644 --- a/rman_operators/rman_operators_stylized.py +++ b/rman_operators/rman_operators_stylized.py @@ -86,11 +86,11 @@ def add_manifolds(self, nt, pattern_node): def attach_pattern(self, context, ob): mat = object_utils.get_active_material(ob) - if not mat: + if mat is None: bpy.ops.object.rman_add_bxdf('EXEC_DEFAULT', bxdf_name='PxrSurface') mat = object_utils.get_active_material(ob) - if not mat: + if mat is None: self.report({'ERROR'}, 'Cannot find a material for: %s' % ob.name) nt = mat.node_tree @@ -144,12 +144,12 @@ def attach_pattern(self, context, ob): val = param_settings['value'] setattr(pattern_node, param_name, val) - array_len = getattr(node, coll_idx_nm) - sub_prop_nm = '%s[%d]' % (prop_name, array_len-1) + idx = getattr(node, coll_idx_nm) + sub_prop_nm = '%s[%d]' % (prop_name, idx) nt.links.new(pattern_node.outputs['resultAOV'], node.inputs[sub_prop_nm]) # Add manifolds - self.add_manifolds(nt, pattern_node) + self.add_manifolds(nt, pattern_node) else: if node.inputs[prop_name].is_linked: From 64c79ff3941c209f3c3fb2d3b50fa0ebcea8fae5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 29 Jul 2022 08:36:22 -0700 Subject: [PATCH 172/278] There's a new color ramp for PxrStylizedToon, so make sure that gets added in our upgrade_250 function. --- rfb_utils/upgrade_utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index e71eab5c..8372d01f 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -33,6 +33,9 @@ def upgrade_250(scene): Projection -> projection_in/projection_out DisplayFilter -> displayfilter_in/displayfilter_out SampleFilter -> samplefilter_in/samplefilter_out + + Add: color ramp to PxrStylizedToon + ''' for node in shadergraph_utils.get_all_shading_nodes(scene=scene): renderman_node_type = getattr(node, 'renderman_node_type', '') @@ -52,6 +55,12 @@ def upgrade_250(scene): elif renderman_node_type == 'displayfilter': if 'DisplayFilter' in node.outputs: node.outputs['DisplayFilter'].name = '%s_out' % renderman_node_type + + if node.bl_label == 'PxrStylizedToon': + nt = node.rman_fake_node_group_ptr + n = nt.nodes.new('ShaderNodeValToRGB') + setattr(node, 'colorRamp', n.name) + for mat in bpy.data.materials: output = shadergraph_utils.find_node(mat, 'RendermanOutputNode') From b7b21e38e975e64a517e23f558631275bf956014 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 29 Jul 2022 09:37:17 -0700 Subject: [PATCH 173/278] When retrieving ramps, look in the bpy.data.node_groups, if we can't seem to find our specific ramp node. --- rfb_utils/property_utils.py | 14 +++++++++----- rfb_utils/upgrade_utils.py | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index 64533c0f..f38b30c4 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -575,9 +575,15 @@ def set_pxrosl_params(node, rman_sg_node, params, ob=None, mat_name=None): set_rix_param(params, param_type, param_name, val, is_reference=False) def set_ramp_rixparams(node, prop_name, prop, param_type, params): + nt = node.rman_fake_node_group_ptr + ramp_name = prop + if nt and ramp_name not in nt.nodes: + # this shouldn't happen, but sometimes can + # try to look at the bpy.data.node_groups version + nt = bpy.data.node_groups[node.rman_fake_node_group] + if nt and ramp_name not in nt.nodes: + nt = None if param_type == 'colorramp': - nt = node.rman_fake_node_group_ptr - ramp_name = prop if nt: color_ramp_node = nt.nodes[ramp_name] colors = [] @@ -625,9 +631,7 @@ def set_ramp_rixparams(node, prop_name, prop, param_type, params): interp = 'catmull-rom' params.SetString("%s_Interpolation" % prop_name, interp ) - elif param_type == 'floatramp': - nt = node.rman_fake_node_group_ptr - ramp_name = prop + elif param_type == 'floatramp': if nt: float_ramp_node = nt.nodes[ramp_name] diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index 8372d01f..e73e26d2 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -57,9 +57,9 @@ def upgrade_250(scene): node.outputs['DisplayFilter'].name = '%s_out' % renderman_node_type if node.bl_label == 'PxrStylizedToon': - nt = node.rman_fake_node_group_ptr - n = nt.nodes.new('ShaderNodeValToRGB') - setattr(node, 'colorRamp', n.name) + nt = node.rman_fake_node_group_ptr + n = nt.nodes.new('ShaderNodeValToRGB') + setattr(node, 'colorRamp', n.name) for mat in bpy.data.materials: From 0d59be6a2d7f78264d71b9c98571d40661eeeeba Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 3 Aug 2022 09:48:47 -0700 Subject: [PATCH 174/278] Typo in in the call to attach_material. --- rman_scene_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index a800187a..42e2498e 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -832,7 +832,7 @@ def check_instances(self, batch_mode=False): if psys: self.rman_scene.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) else: - self.attach_material(ob_eval, rman_sg_group) + self.rman_scene.attach_material(ob_eval, rman_sg_group) if not batch_mode: if rman_type == 'LIGHT': From 51dd9836f6bac59a75e1d2c21969df155942ef7f Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 3 Aug 2022 14:18:33 -0700 Subject: [PATCH 175/278] Check to make sure sg_material_node is not None in set_material. --- rfb_utils/scenegraph_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index 0972f2fd..4d95fb8b 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -7,6 +7,8 @@ def set_material(sg_node, sg_material_node): sg_material_node (RixSGMaterial) - the scene graph material node ''' + if sg_material_node is None: + return sg_node.SetMaterial(sg_material_node) attrs = sg_node.GetAttributes() From 00b230f1cb3684635b6b39586ba02cdf7eac9050 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 4 Aug 2022 07:21:16 -0700 Subject: [PATCH 176/278] * rename preload_xpu to preload_dsos. We now seem to crash blender on linux when using impl_openvdb, so we do the same thing as xpu by preloading it. * in openvdb translator, double check the size of the grids array is > 1. --- rman_render.py | 33 ++++++++++++++------- rman_translators/rman_openvdb_translator.py | 6 ++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/rman_render.py b/rman_render.py index a4692efa..5cc8ce5d 100644 --- a/rman_render.py +++ b/rman_render.py @@ -219,29 +219,37 @@ def live_render_cb(e, d, db): else: db.rman_is_refining = True -def preload_xpu(): +def preload_dsos(rman_render): """On linux there is a problem with std::call_once and blender, by default, being linked with a static libstdc++. The loader seems to not be able to get the right tls key - for the __once_call global when libprman loads libxpu. By preloading + for the __once_call global when libprman loads libxpu etc. By preloading we end up calling the proxy in the blender executable and that works. + + Arguments: + rman_render (RmanRender) - instance of RmanRender where we want to store + the ctypes.CDLL - Returns: - ctypes.CDLL of xpu or None if that fails. None if not on linux - """ + """ if sys.platform != 'linux': - return None + return - tree = envconfig().rmantree + tree = envconfig().rmantree xpu_path = os.path.join(tree, 'lib', 'libxpu.so') + implopenvdb_path = os.path.join(tree, 'lib', 'plugins', 'impl_openvdb.so') try: - xpu = ctypes.CDLL(xpu_path) - return xpu + rman_render.preload_xpu = ctypes.CDLL(xpu_path) except OSError as error: rfb_log().debug('Failed to preload xpu: {0}'.format(error)) - return None + return + + try: + rman_render.preload_impl_openvdb = ctypes.CDLL(implopenvdb_path) + except OSError as error: + rfb_log().debug('Failed to preload openvdb: {0}'.format(error)) + return class BlRenderResultHelper: def __init__(self, rman_render, dspy_dict): @@ -410,7 +418,10 @@ def __init__(self): self._start_prman_begin() # hold onto this or python will unload it - self.preload_xpu = preload_xpu() + self.preload_xpu = None + self.preload_impl_openvdb = None + + preload_dsos(self) @classmethod def get_rman_render(self): diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index f1f8fd4e..6ce05b26 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -57,6 +57,12 @@ def update(self, ob, rman_sg_openvdb): rman_sg_openvdb.sg_node.SetPrimVars(primvar) return + if len(grids) < 1: + rfb_log().error("Grids length=0: %s" % ob.name) + primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "box") + rman_sg_openvdb.sg_node.SetPrimVars(primvar) + return + active_index = grids.active_index active_grid = grids[active_index] if active_grid.data_type not in ['FLOAT', 'DOUBLE']: From 48a7248ff7bd43e0f4a5c9e7a45ff9c605a797ef Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 4 Aug 2022 08:18:59 -0700 Subject: [PATCH 177/278] * fix a typo in rman_scene_sync (update_prop_name -> updated_name_prop) * remove a couple of unused module imports. --- rman_scene_sync.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 42e2498e..5b23a49b 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -818,9 +818,9 @@ def check_instances(self, batch_mode=False): rm = ob_eval.renderman if is_empty_instancer: rm = instance_parent.renderman - meta = rm.prop_meta[rman_update.update_prop_name] - - property_utils.set_riattr_bl_prop(attrs, rman_update.update_prop_name, meta, rm, check_inherit=True) + meta = rm.prop_meta[rman_update.updated_prop_name] + rfb_log().debug("Setting RiAttribute: %s" % rman_update.updated_prop_name) + property_utils.set_riattr_bl_prop(attrs, rman_update.updated_prop_name, meta, rm, check_inherit=True) rman_sg_group.sg_node.SetAttributes(attrs) if rman_update.is_updated_transform: @@ -970,8 +970,7 @@ def update_root_node_func(self, prop_name, context): def update_sg_node_riattr(self, prop_name, context): if not self.rman_render.rman_interactive_running: - return - from .rfb_utils import property_utils + return self.rman_scene.bl_scene = context.scene ob = context.object rman_update = RmanUpdate() @@ -983,8 +982,7 @@ def update_sg_node_riattr(self, prop_name, context): def update_sg_node_primvar(self, prop_name, context): if not self.rman_render.rman_interactive_running: - return - from .rfb_utils import property_utils + return self.rman_scene.bl_scene = context.scene ob = context.object rman_update = RmanUpdate() From 3186baa24d87998f4f7f5e3370f58948eaabeaab Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 17 Aug 2022 10:51:52 -0700 Subject: [PATCH 178/278] Add icon for LamaIridescence. --- rfb_icons/out_LamaIridescence.png | Bin 0 -> 2424 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 rfb_icons/out_LamaIridescence.png diff --git a/rfb_icons/out_LamaIridescence.png b/rfb_icons/out_LamaIridescence.png new file mode 100644 index 0000000000000000000000000000000000000000..c9465caeb1c2f0ac380eafe85374504a8701ad74 GIT binary patch literal 2424 zcmV-;35WKHP)0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU*8c9S!RCwC#TG?_NxfR9Q3m1u6#}j2T zo~bNVNhN>a^*j8h`Gh>=VO~;6YEp5W@g_^6M6wq&fE)nbAf2%#ilk(_N-*8*Zi20Q z`kr$aH0iTv&&ZcN82Q4-9e_IkcL44H+yPi?h@G7syjg3FF-_B8UuzxqaqGNrG7)gVK^`R2w;E#NR(38`HTSIf3Q^(+A!KsW;_CF zjnG;c9l>xv;4!#D`SCgU#CgERJC7|ThA<#els1V20}NTCvZe-}0ERJ^_yJecb*=sR zPfQ`=Eejy<&u#pnWvt-*BFW$xAQ@H#UWsl3&ESwM%dit1@=0LKDMUM~X$yk1ZVCH@ zlw3~x$&2AmuIim)vOl|s?2=MKD!_32v1?8t+9IKWNHl=yu=)KK5*%`t^?n>3+?$RM zCSwpRTnnU%Fg^i=;0tbaLP$fL07}LiE?fW5xgVl?G~>^=_kSE69!$sIoxaIbjT;f< z0T|H6?MPfi)*I_!FiRvclA2sM+o~#=m9=7MgTl`@BkJ=#dFg=j7WB$8UO}J5+KPDGd%J-FNN&gvtp{k}kT=*y>un@S_#8+CX)gzi|w$2cqeWP+ zU>l9RqTU;7Knct3^t?&4c4;`K$~v;*5v_1QThxN z8C5KAXkw`LGA3|{R7e;Onc-0z?yI1a&InTn<@o60>?H5kT(AxRq&$=9Mc#uSXjcwy z$`AbUBDn>LRNMSI!ml!h%5+z_6ueS-@azd?6^M zTIIfm1%@^AbPM203}L5Xe&f`5=y|J1m?;W#5A#07qk=fYGP;stcC$Q7t;Q1)a+Mm(6GajPbE3e zA?pxfJqA~=b8u-?%|ImWSWki&p!SCGL0zpwBo-su$9ArqfKdDAqv~HnFpK=@R2~(A zrc-3SdG}^r?If-J-RWy?O_%-L=d&cc$a>*wXysN}8(qf4D<@$5<)HreK;z&KC878 zF;A!9)t0KhnVivuW&@?gX>SlNBBhM{+!%$fh2e)?1zF?x_v55*tV?tAu!^!U#T(kX zfvQN?U*pvemg}|+@NrpuP|B8tXt{0e{sTn-$c8h z05KTG!xGcAoWPd1Y{{9LkstP&hLT*fuM19PO;C-KSf2<=T$_w;ZgFt{M4F~qxDGI^ z;(^U#_{Yc*X{7kJVl1iRN||p>-8~-G)lgO4nwj&360-4XzLpc%bs4hcOq75x`z|pZ zAYDPpvWhEw9)mL!VhCM-yBJ7FDU)y!G(9Vdc!L;T_smQpVNui?E-f(AuD`nc#<5MT z8P!E6W%mj48eZ4R32Zf`E{(zUrkSC+VL{Cc6P%%YRUwUO*IoTbXg<P zYtA6QV8ghfi{Z!{&~NI6`F>T|cbm&Nfo&xFUOp2PLaTrS#3W}sH{|_q;6C@XT70l# zybP8pD>hb*UOuxF?v48gac-roduYH#S9~H8EJacxIt4>P+=ch}O373(s+!HNyd`kH1el4VYzMNLN_H`UOriGj3_;p{?T z3;J@!Vd`O)f!mm qrrty!_rb5+U_)~Y7%mzA6JP)qk$Zv^L8}!200006i literal 0 HcmV?d00001 From 1c9560f7077ead8185f839540b7868460e58d4e8 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 19 Aug 2022 15:25:38 -0700 Subject: [PATCH 179/278] When reverting back to Blender displays, make sure the display channels collection is also cleared. --- rman_ui/rman_ui_aovs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_aovs.py b/rman_ui/rman_ui_aovs.py index ca1f1985..b2da106a 100644 --- a/rman_ui/rman_ui_aovs.py +++ b/rman_ui/rman_ui_aovs.py @@ -419,7 +419,8 @@ class PRMAN_OT_revert_renderman_aovs(bpy.types.Operator): def execute(self, context): active_layer = context.view_layer rm_rl = active_layer.renderman - rm_rl.custom_aovs.clear() + rm_rl.custom_aovs.clear() + rm_rl.dspy_channels.clear() rm_rl.use_renderman = False return {'FINISHED'} From 42e56031aef4b2d6b411ea5edb7f163b615b203c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Sep 2022 09:00:45 -0700 Subject: [PATCH 180/278] Make sure to check for licenses before starting an interactive render. --- rman_render.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rman_render.py b/rman_render.py index 5cc8ce5d..de371c22 100644 --- a/rman_render.py +++ b/rman_render.py @@ -998,9 +998,11 @@ def start_interactive_render(self, context, depsgraph): global __DRAW_THREAD__ self.reset() - self.rman_interactive_running = True - self.rman_running = True __update_areas__() + if not self._check_prman_license(): + return False + self.rman_interactive_running = True + self.rman_running = True self.bl_scene = depsgraph.scene_eval rm = depsgraph.scene_eval.renderman self.it_port = start_cmd_server() From 0d98cd1695e2cc55546fb696548a6a0c458fb836 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Sep 2022 10:12:25 -0700 Subject: [PATCH 181/278] If an empty instancer updated, make sure we update all objects in the instance collection. --- rman_scene_sync.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 5b23a49b..aeb25967 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -375,7 +375,23 @@ def update_collection(self, coll): rman_update = RmanUpdate() rman_update.is_updated_shading = True rman_update.is_updated_transform = True - self.rman_updates[o.original] = rman_update + self.rman_updates[o.original] = rman_update + + def check_empty_instancer(self, dps_update): + # mark all objects in a collection + # as needing their instances updated + ob = dps_update.id.evaluated_get(self.rman_scene.depsgraph) + coll = ob.instance_collection + rfb_log().debug("\tEmpty Instancer %s Updated" % ob.name) + for o in coll.all_objects: + if o.type in ('ARMATURE', 'CAMERA'): + continue + if o.original not in self.rman_updates: + rman_update = RmanUpdate() + rman_update.is_updated_geometry = dps_update.is_updated_geometry + rman_update.is_updated_shading = dps_update.is_updated_shading + rman_update.is_updated_transform = dps_update.is_updated_transform + self.rman_updates[o.original] = rman_update def update_portals(self, ob): for portal in scene_utils.get_all_portals(ob): @@ -419,6 +435,8 @@ def check_object_datablock(self, dps_update): self.light_filter_updated(dps_update) elif rman_type == 'CAMERA': self.camera_updated(dps_update) + elif rman_type == 'EMPTY_INSTANCER': + self.check_empty_instancer(dps_update) else: rfb_log().debug("\tObject: %s Updated" % dps_update.id.name) rfb_log().debug("\t is_updated_geometry: %s" % str(dps_update.is_updated_geometry)) From dd4e6ca848d3f0ec028148d14fc114fd49324c1b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Sep 2022 11:53:44 -0700 Subject: [PATCH 182/278] If an instance is part of an empty instancer, add the sg node as a child of the empty instancer node. When the empty instancer is edited, we clear out all of the children first. This is in case the collection that is being instanced has changed. --- rman_scene.py | 9 ++++++++- rman_scene_sync.py | 12 +++++++++--- rman_translators/rman_empty_translator.py | 5 +++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index 7601035d..bded6364 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -614,7 +614,7 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa is_empty_instancer = False if instance_parent: is_empty_instancer = object_utils.is_empty_instancer(instance_parent) - + # Object attrs translator = self.rman_translators.get(rman_type, None) if translator: @@ -653,6 +653,11 @@ def export_instance(self, ob_eval, ob_inst, rman_sg_node, rman_type, instance_pa rman_empty_node = self.get_rman_prototype(parent_proto_key, ob=ob_parent_eval, create=True) rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform rman_empty_node.sg_node.AddChild(rman_sg_group.sg_node) + elif is_empty_instancer: + parent_proto_key = object_utils.prototype_key(instance_parent) + rman_parent_node = self.get_rman_prototype(parent_proto_key, ob=instance_parent, create=True) + rman_sg_group.sg_node.SetInheritTransform(False) # we don't want to inherit the transform + rman_parent_node.sg_node.AddChild(rman_sg_group.sg_node) else: self.get_root_sg_node().AddChild(rman_sg_group.sg_node) @@ -793,6 +798,8 @@ def export_data_block(self, proto_key, ob): # along with any instance attributes/materials necessary self._export_hidden_instance(ob, rman_sg_node) return rman_sg_node + elif rman_type == 'EMPTY_INSTANCER': + self.get_root_sg_node().AddChild(rman_sg_node.sg_node) return rman_sg_node diff --git a/rman_scene_sync.py b/rman_scene_sync.py index aeb25967..03737ecd 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -378,11 +378,17 @@ def update_collection(self, coll): self.rman_updates[o.original] = rman_update def check_empty_instancer(self, dps_update): - # mark all objects in a collection - # as needing their instances updated ob = dps_update.id.evaluated_get(self.rman_scene.depsgraph) coll = ob.instance_collection - rfb_log().debug("\tEmpty Instancer %s Updated" % ob.name) + rfb_log().debug("\tEmpty Instancer %s Updated" % ob.name) + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + proto_key = object_utils.prototype_key(ob) + rman_sg_node = self.rman_scene.get_rman_prototype(proto_key, ob=ob, create=True) + rman_type = object_utils._detect_primitive_(ob) + self.rman_scene.rman_translators[rman_type].clear_children(rman_sg_node) + + # mark all objects in the instance collection + # as needing their instances updated for o in coll.all_objects: if o.type in ('ARMATURE', 'CAMERA'): continue diff --git a/rman_translators/rman_empty_translator.py b/rman_translators/rman_empty_translator.py index 28a7b7be..a080ac7f 100644 --- a/rman_translators/rman_empty_translator.py +++ b/rman_translators/rman_empty_translator.py @@ -19,6 +19,11 @@ def update_transform_sample(self, ob, rman_sg_group, index, seg): def update_transform_num_samples(self, rman_sg_group, motion_steps): pass + def clear_children(self, rman_sg_group): + if rman_sg_group.sg_node: + for c in [ rman_sg_group.sg_node.GetChild(i) for i in range(0, rman_sg_group.sg_node.GetNumChildren())]: + rman_sg_group.sg_node.RemoveChild(c) + def export(self, ob, db_name=""): sg_group = self.rman_scene.sg_scene.CreateGroup(db_name) rman_sg_group = RmanSgGroup(self.rman_scene, sg_group, db_name) From 47378c39bcd86f34a30efd6a96e0a8fa1b948ffd Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Sep 2022 15:16:55 -0700 Subject: [PATCH 183/278] Add a dictionary to the material translator to map cycles node parameters to their node property names. --- rman_translators/rman_material_translator.py | 25 ++++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/rman_translators/rman_material_translator.py b/rman_translators/rman_material_translator.py index 93efa5cb..e39618d8 100644 --- a/rman_translators/rman_material_translator.py +++ b/rman_translators/rman_material_translator.py @@ -13,6 +13,15 @@ import re import bpy +__MAP_CYCLES_PARAMS__ = { + "ShaderNodeTexNoise": { + "dimensions": "noise_dimensions" + }, + "ShaderNodeAttribute": { + "name": "attribute_name" + } +} + def get_root_node(node, type='bxdf'): rman_type = getattr(node, 'renderman_node_type', node.bl_idname) if rman_type == type: @@ -26,6 +35,13 @@ def get_root_node(node, type='bxdf'): return out return None +def get_cycles_value(node, param_name): + val = getattr(node, param_name, None) + if node.bl_idname in __MAP_CYCLES_PARAMS__: + params_map = __MAP_CYCLES_PARAMS__[node.bl_idname] + val = getattr(node, params_map.get(param_name, param_name), None) + return val + class RmanMaterialTranslator(RmanTranslator): def __init__(self, rman_scene): @@ -369,12 +385,11 @@ def translate_cycles_node(self, mat, rman_sg_material, node, mat_name, group_nod param_name = node_desc_param._name if param_name in node.inputs: continue - if param_name == 'name': - param_name = 'attribute_name' - if not hasattr(node, param_name): - continue param_type = node_desc_param.type - val = string_utils.convert_val(getattr(node, param_name), + val = get_cycles_value(node, param_name) + if val is None: + continue + val = string_utils.convert_val(val, type_hint=param_type) if param_type == 'string': From 7752f1118280ae902ace80f25ad925086aeb53e5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Sep 2022 15:35:38 -0700 Subject: [PATCH 184/278] Forgot a parameter in the override methods for export_object_attributes in light and lightfilter translators --- rman_translators/rman_light_translator.py | 2 +- rman_translators/rman_lightfilter_translator.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_translators/rman_light_translator.py b/rman_translators/rman_light_translator.py index 1e7db25c..c6ee2197 100644 --- a/rman_translators/rman_light_translator.py +++ b/rman_translators/rman_light_translator.py @@ -40,7 +40,7 @@ def __init__(self, rman_scene): super().__init__(rman_scene) self.bl_type = 'LIGHT' - def export_object_attributes(self, ob, rman_sg_node): + def export_object_attributes(self, ob, rman_sg_node, remove=True): pass def export(self, ob, db_name): diff --git a/rman_translators/rman_lightfilter_translator.py b/rman_translators/rman_lightfilter_translator.py index c9fb3ebd..0e808d2e 100644 --- a/rman_translators/rman_lightfilter_translator.py +++ b/rman_translators/rman_lightfilter_translator.py @@ -11,7 +11,7 @@ def __init__(self, rman_scene): super().__init__(rman_scene) self.bl_type = 'LIGHTFILTER' - def export_object_attributes(self, ob, rman_sg_node): + def export_object_attributes(self, ob, rman_sg_node, remove=True): pass def export_light_filters(self, ob, rman_sg_node, rm): From 16465c3acf4327d2105b4686babdc1592be612c5 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 2 Sep 2022 08:39:39 -0700 Subject: [PATCH 185/278] * bump version to 25.0 * add a button to upgrade scene in case RenderMan wasn't the render engine when the scene was saved. --- __init__.py | 6 +++--- rfb_utils/upgrade_utils.py | 2 ++ rman_constants.py | 4 ++-- rman_operators/rman_operators_utils.py | 30 ++++++++++++++++++++++++++ rman_ui/rman_ui_view3d_panels.py | 1 + 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/__init__.py b/__init__.py index 04c892d5..7bcf1e57 100644 --- a/__init__.py +++ b/__init__.py @@ -30,12 +30,12 @@ bl_info = { "name": "RenderMan For Blender", "author": "Pixar", - "version": (24, 4, 0), + "version": (25, 0, 0), "blender": (2, 83, 0), "location": "Render Properties > Render Engine > RenderMan", - "description": "RenderMan 24 integration", + "description": "RenderMan 25 integration", "doc_url": "https://rmanwiki.pixar.com/display/RFB", - "warning": "", + "warning": "BETA 1", "category": "Render"} __RMAN_ADDON_LOADED__ = False diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index e73e26d2..fbbb114b 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -127,6 +127,8 @@ def upgrade_scene(bl_scene): if version < version_str: rfb_log().debug('Upgrade scene to %s' % version_str) fn(scene) + + scene.renderman.renderman_version = rman_constants.RMAN_SUPPORTED_VERSION_STRING def update_version(bl_scene): if bpy.context.engine != 'PRMAN_RENDER': diff --git a/rman_constants.py b/rman_constants.py index ae10fb77..918fcc51 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -25,8 +25,8 @@ BLENDER_PYTHON_VERSION_MINOR = pyver.minor BLENDER_PYTHON_VERSION = '%s.%s' % (pyver.major, pyver.minor) -RMAN_SUPPORTED_VERSION_MAJOR = 24 -RMAN_SUPPORTED_VERSION_MINOR = 4 +RMAN_SUPPORTED_VERSION_MAJOR = 25 +RMAN_SUPPORTED_VERSION_MINOR = 0 RMAN_SUPPORTED_VERSION_BETA = '' RMAN_SUPPORTED_VERSION = (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR, RMAN_SUPPORTED_VERSION_BETA) RMAN_SUPPORTED_VERSION_STRING = '%d.%d%s' % (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR, RMAN_SUPPORTED_VERSION_BETA) diff --git a/rman_operators/rman_operators_utils.py b/rman_operators/rman_operators_utils.py index a582558b..ea34ea17 100644 --- a/rman_operators/rman_operators_utils.py +++ b/rman_operators/rman_operators_utils.py @@ -4,6 +4,8 @@ from ..rfb_utils import texture_utils from ..rfb_utils import filepath_utils from ..rfb_utils import object_utils +from ..rfb_utils import upgrade_utils +from .. import rman_constants from bpy.types import Operator from bpy.props import StringProperty, FloatProperty import os @@ -11,6 +13,33 @@ import bpy import shutil +class PRMAN_OT_Renderman_Upgrade_Scene(Operator): + """An operator to upgrade the scene to the current version of RenderMan.""" + + bl_idname = "renderman.upgrade_scene" + bl_label = "Upgrade Scene" + bl_description = "Upgrade your scene to the current version" + bl_options = {'INTERNAL'} + + @classmethod + def poll(cls, context): + if context.engine != "PRMAN_RENDER": + return False + + scene = context.scene + version = scene.renderman.renderman_version + if version == '': + return True + + if version < rman_constants.RMAN_SUPPORTED_VERSION_STRING: + return True + + return False + + def execute(self, context): + upgrade_utils.upgrade_scene(None) + return {'FINISHED'} + class PRMAN_OT_Renderman_Package(Operator): """An operator to create a zip archive of the current scene.""" @@ -249,6 +278,7 @@ def invoke(self, context, event): return {'RUNNING_MODAL'} classes = [ + PRMAN_OT_Renderman_Upgrade_Scene, PRMAN_OT_Renderman_Package, PRMAN_OT_Renderman_Start_Debug_Server ] diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index 1bf2b6f6..45103109 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -241,6 +241,7 @@ def draw(self, context): op.module = "RenderManForBlender" rman_pack_scene = rfb_icons.get_icon('rman_package_scene') box.operator("renderman.scene_package", icon_value=rman_pack_scene.icon_id) + box.operator("renderman.upgrade_scene", icon='FILE_REFRESH') layout.separator() # RenderMan Doc From dbe3ab855eb3bfd6ef8ab4d5a86282f03132f4b0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 2 Sep 2022 09:41:43 -0700 Subject: [PATCH 186/278] Modify preload_dsos to loop over a list of DSOs we want to preload. Append to a list on the RmanRender instance. --- rman_render.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rman_render.py b/rman_render.py index de371c22..1f4839ae 100644 --- a/rman_render.py +++ b/rman_render.py @@ -235,21 +235,20 @@ def preload_dsos(rman_render): if sys.platform != 'linux': return + plugins = [ + 'lib/libxpu.so', + 'lib/plugins/impl_openvdb.so', + ] + tree = envconfig().rmantree - xpu_path = os.path.join(tree, 'lib', 'libxpu.so') - implopenvdb_path = os.path.join(tree, 'lib', 'plugins', 'impl_openvdb.so') - try: - rman_render.preload_xpu = ctypes.CDLL(xpu_path) - except OSError as error: - rfb_log().debug('Failed to preload xpu: {0}'.format(error)) - return + for plugin in plugins: + plugin_path = os.path.join(tree, plugin) + try: + rman_render.preloaded_dsos.append(ctypes.CDLL(plugin_path)) + except OSError as error: + rfb_log().debug('Failed to preload {0}: {1}'.format(plugin_path, error)) - try: - rman_render.preload_impl_openvdb = ctypes.CDLL(implopenvdb_path) - except OSError as error: - rfb_log().debug('Failed to preload openvdb: {0}'.format(error)) - return class BlRenderResultHelper: def __init__(self, rman_render, dspy_dict): @@ -420,6 +419,7 @@ def __init__(self): # hold onto this or python will unload it self.preload_xpu = None self.preload_impl_openvdb = None + self.preloaded_dsos = list() preload_dsos(self) From 68b48dbf814ddc776d1da1585ffd9e12f0fbdfcd Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 2 Sep 2022 09:43:24 -0700 Subject: [PATCH 187/278] Remove some members that are no longer needed from RmanRender. --- rman_render.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rman_render.py b/rman_render.py index 1f4839ae..77b0f292 100644 --- a/rman_render.py +++ b/rman_render.py @@ -417,8 +417,6 @@ def __init__(self): self._start_prman_begin() # hold onto this or python will unload it - self.preload_xpu = None - self.preload_impl_openvdb = None self.preloaded_dsos = list() preload_dsos(self) From 7fa6787eb7c02de795c0aaa7d19346874636b395 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 19 Sep 2022 11:59:42 -0700 Subject: [PATCH 188/278] Fix typo in rman_bl_nodes/__init__.py idenitifer -> identifier --- rman_bl_nodes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_bl_nodes/__init__.py b/rman_bl_nodes/__init__.py index 353469d1..6aea7f29 100644 --- a/rman_bl_nodes/__init__.py +++ b/rman_bl_nodes/__init__.py @@ -338,7 +338,7 @@ def init(self, context): self.outputs.new('RendermanNodeSocketLight', "light_out", identifier="Light") elif self.renderman_node_type == 'lightfilter': node_add_inputs(self, name, self.prop_names) - self.outputs.new('RendermanNodeSocketLightFilter', "lightfilter_out", idenitifer="LightFilter") + self.outputs.new('RendermanNodeSocketLightFilter', "lightfilter_out", identifier="LightFilter") elif self.renderman_node_type == 'displace': self.outputs.new('RendermanNodeSocketDisplacement', "displace_out", identifier="Displacement") node_add_inputs(self, name, self.prop_names) From 272d324643e25f2ccf045ada2542755d9e73b20c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 19 Sep 2022 12:49:04 -0700 Subject: [PATCH 189/278] Blender's float ramp widget doesn't have an interpolation selection. We should still allow the user to select a different interpolation, so display our version of the interpolation parameter. --- rfb_utils/draw_utils.py | 8 +- rfb_utils/generate_property_utils.py | 111 +++++++++++++------------ rfb_utils/property_utils.py | 7 +- rman_bl_nodes/rman_bl_nodes_shaders.py | 7 +- 4 files changed, 74 insertions(+), 59 deletions(-) diff --git a/rfb_utils/draw_utils.py b/rfb_utils/draw_utils.py index 25c2c3e5..1e140920 100644 --- a/rfb_utils/draw_utils.py +++ b/rfb_utils/draw_utils.py @@ -379,7 +379,7 @@ def draw_prop(node, prop_name, layout, level=0, nt=None, context=None, sticky=Fa ramp_node = node_group.nodes[ramp_name] layout.enabled = (nt.library is None) layout.template_color_ramp( - ramp_node, 'color_ramp') + ramp_node, 'color_ramp') return elif bl_prop_info.widget == 'floatramp': node_group = node.rman_fake_node_group_ptr @@ -392,7 +392,11 @@ def draw_prop(node, prop_name, layout, level=0, nt=None, context=None, sticky=Fa ramp_node = node_group.nodes[ramp_name] layout.enabled = (nt.library is None) layout.template_curve_mapping( - ramp_node, 'mapping') + ramp_node, 'mapping') + + interp_name = '%s_Interpolation' % prop_name + if hasattr(node, interp_name): + layout.prop(node, interp_name, text='Ramp Interpolation') return elif bl_prop_info.widget == 'displaymetadata': diff --git a/rfb_utils/generate_property_utils.py b/rfb_utils/generate_property_utils.py index 1e0409ec..c95e6999 100644 --- a/rfb_utils/generate_property_utils.py +++ b/rfb_utils/generate_property_utils.py @@ -46,6 +46,61 @@ def colorspace_names_list(): pass return items +def generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function): + prop = None + if 'ocio_colorspaces' in sp.options: + def colorspace_names_options(self, context): + items = [] + items.append(('Disabled', 'Disabled', '')) + items.extend(colorspace_names_list()) + return items + + prop = EnumProperty(name=param_label, + description=param_help, + items=colorspace_names_options, + set=set_function, + update=update_function) + else: + items = [] + + if param_default == '' or param_default == "''": + param_default = __RMAN_EMPTY_STRING__ + + in_items = False + + if isinstance(sp.options, list): + for v in sp.options: + if v == '' or v == "''": + v = __RMAN_EMPTY_STRING__ + items.append((str(v), str(v), '')) + if param_default == str(v): + in_items = True + else: + for k,v in sp.options.items(): + if v == '' or v == "''": + v = __RMAN_EMPTY_STRING__ + items.append((str(v), str(k), '')) + if param_default == str(v): + in_items = True + + if in_items: + prop = EnumProperty(name=param_label, + default=param_default, description=param_help, + items=items, + set=set_function, + update=update_function) + else: + # for strings, assume the first item is the default + k = items[0][1] + items[0] = (param_default, k, '' ) + prop = EnumProperty(name=param_label, + default=param_default, description=param_help, + items=items, + set=set_function, + update=update_function) + + return prop + def generate_colorspace_menu(node, param_name): '''Generate a colorspace enum property for the incoming parameter name @@ -445,63 +500,15 @@ def generate_property(node, sp, update_function=None, set_function=None): description=param_help) elif param_widget in ['mapper', 'popup']: - if 'ocio_colorspaces' in sp.options: - def colorspace_names_options(self, context): - items = [] - items.append(('Disabled', 'Disabled', '')) - items.extend(colorspace_names_list()) - return items - - prop = EnumProperty(name=param_label, - description=param_help, - items=colorspace_names_options, - set=set_function, - update=update_function) - else: - items = [] - - if param_default == '' or param_default == "''": - param_default = __RMAN_EMPTY_STRING__ - - in_items = False - - if isinstance(sp.options, list): - for v in sp.options: - if v == '' or v == "''": - v = __RMAN_EMPTY_STRING__ - items.append((str(v), str(v), '')) - if param_default == str(v): - in_items = True - else: - for k,v in sp.options.items(): - if v == '' or v == "''": - v = __RMAN_EMPTY_STRING__ - items.append((str(v), str(k), '')) - if param_default == str(v): - in_items = True - - if in_items: - prop = EnumProperty(name=param_label, - default=param_default, description=param_help, - items=items, - set=set_function, - update=update_function) - else: - # for strings, assume the first item is the default - k = items[0][1] - items[0] = (param_default, k, '' ) - prop = EnumProperty(name=param_label, - default=param_default, description=param_help, - items=items, - set=set_function, - update=update_function) + prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function) elif param_widget == 'bl_scenegraphlocation': reference_type = eval(sp.options['nodeType']) prop = PointerProperty(name=param_label, description=param_help, - type=reference_type) - + type=reference_type) + elif param_widget == 'null' and hasattr(sp, 'options'): + prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function) else: prop = StringProperty(name=param_label, default=str(param_default), diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index f38b30c4..e289fa38 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -651,8 +651,8 @@ def set_ramp_rixparams(node, prop_name, prop, param_type, params): params.SetFloatArray('%s_Knots' % prop_name, knots, len(knots)) params.SetFloatArray('%s_Floats' % prop_name, vals, len(vals)) - # Blender doesn't have an interpolation selection for float ramps. Default to catmull-rom - interp = 'catmull-rom' + interp_name = '%s_Interpolation' % prop_name + interp = getattr(node, interp_name, 'linear') params.SetString("%s_Interpolation" % prop_name, interp ) else: # this might be from a linked file @@ -677,7 +677,8 @@ def set_ramp_rixparams(node, prop_name, prop, param_type, params): params.SetFloatArray('%s_Knots' % prop_name, knots, len(knots)) params.SetFloatArray('%s_Floats' % prop_name, vals, len(vals)) - interp = 'catmull-rom' + interp_name = '%s_Interpolation' % prop_name + interp = getattr(node, interp_name, 'linear') params.SetString("%s_Interpolation" % prop_name, interp ) def set_array_rixparams(node, rman_sg_node, mat_name, bl_prop_info, prop_name, prop, params): diff --git a/rman_bl_nodes/rman_bl_nodes_shaders.py b/rman_bl_nodes/rman_bl_nodes_shaders.py index 9d260374..2aa2ac92 100644 --- a/rman_bl_nodes/rman_bl_nodes_shaders.py +++ b/rman_bl_nodes/rman_bl_nodes_shaders.py @@ -179,7 +179,7 @@ def draw_nonconnectable_prop(self, context, layout, prop_name, output_node=None, nt = node.id_data layout.enabled = (nt.library is None) layout.template_color_ramp( - ramp_node, 'color_ramp') + ramp_node, 'color_ramp') return elif bl_prop_info.widget == 'floatramp': node_group = self.rman_fake_node_group_ptr @@ -197,7 +197,10 @@ def draw_nonconnectable_prop(self, context, layout, prop_name, output_node=None, nt = node.id_data layout.enabled = (nt.library is None) layout.template_curve_mapping( - ramp_node, 'mapping') + ramp_node, 'mapping') + interp_name = '%s_Interpolation' % prop_name + if hasattr(node, interp_name): + layout.prop(node, interp_name, text='Ramp Interpolation') return if prop_name not in node.inputs: From 76b33afed36331c33e4ca12cd93593f61c90893c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 21 Sep 2022 09:53:03 -0700 Subject: [PATCH 190/278] Make sure when we do a preview render to "blender" we write out the files to disk. --- rman_render.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rman_render.py b/rman_render.py index 77b0f292..b29c384a 100644 --- a/rman_render.py +++ b/rman_render.py @@ -347,7 +347,7 @@ def finish_passes(self): filepath = '%s_beauty_raw.exr' % (toks[0]) if use_ice: - buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, raw_buffer=True, as_flat=True) + buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, raw_buffer=True, as_flat=False) if buffer is None: continue @@ -694,7 +694,10 @@ def start_render(self, depsgraph, for_background=False): if self.rman_render_into == 'blender': dspy_dict = display_utils.get_dspy_dict(self.rman_scene, include_holdouts=False) bl_rr_helper = BlRenderResultHelper(self, dspy_dict) - bl_rr_helper.write_aovs = (use_compositor and rm.use_bl_compositor_write_aovs) + if for_background: + bl_rr_helper.write_aovs = (use_compositor and rm.use_bl_compositor_write_aovs) + else: + bl_rr_helper.write_aovs = True bl_rr_helper.register_passes() self.start_stats_thread() From 1774fa1aef344a08c77b82f1df681ae65920b159 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 22 Sep 2022 09:59:51 -0700 Subject: [PATCH 191/278] Add "volume:aggregatespace" option. --- rman_config/config/rman_properties_scene.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index b006a2b5..d86b536e 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -985,6 +985,17 @@ "conditionalVisValue": "spiral" } }, + { + "panel": "RENDER_PT_renderman_advanced_settings", + "name": "volume_aggregatespace", + "label": "Volume Aggregate Space", + "riopt": "volume:aggregatespace", + "type": "string", + "default": "world", + "widget": "popup", + "help": "Space in which to compute aggregate volume metadata. Valid spaces are 'world' and 'camera'. Generally the space should be chosen to minimize the range of motion of the volumes.", + "options": "world|camera" + }, { "panel": "RENDER_PT_renderman_advanced_settings", "page": "Open Shading Language", From b8b24ad77b9e00518b2c2d838e129ffbf0d8465b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 27 Sep 2022 11:44:55 -0700 Subject: [PATCH 192/278] Add some new create functions to the rfb_api module. This should be useful for regression testing. --- rfb_api/__init__.py | 261 +++++++++++++++++++++++- rman_operators/rman_operators_view3d.py | 2 +- 2 files changed, 260 insertions(+), 3 deletions(-) diff --git a/rfb_api/__init__.py b/rfb_api/__init__.py index ac8a5526..66f65747 100644 --- a/rfb_api/__init__.py +++ b/rfb_api/__init__.py @@ -1,9 +1,266 @@ from .. import rman_config from .. import rman_bl_nodes +from ..rfb_utils import shadergraph_utils +from ..rfb_utils import object_utils +from ..rfb_logger import rfb_log import bpy -import json -import pprint import os +import json + +def create_light(node_type): + '''Create a RenderMan light + + Arguments: + node_type (str) - name of the RenderMan light caller wants to create (ex: PxrRectLight) + + Returns: + (bpy.types.Object, bpy.types.Node) - the light object and light shader node + + ''' + bpy.ops.object.rman_add_light(rman_light_name=node_type) + ob = bpy.context.object + shader = ob.data.renderman.get_light_node() + return (ob, shader) + +def create_lightfilter(node_type): + '''Create a RenderMan light filter + + Arguments: + node_type (str) - name of the RenderMan light filter caller wants to create (ex: PxrRectLight) + + Returns: + (bpy.types.Object, bpy.types.Node) - the lightfilter object and lightfilter shader node + + ''' + bpy.ops.object.rman_add_light_filter(rman_lightfilter_name=node_type, add_to_selected=False) + ob = bpy.context.object + shader = ob.data.renderman.get_light_node() + return (ob, shader) + +def attach_lightfilter(light, lightfilter): + '''Attach a lightfilter to a light + + Arguments: + light (bpy.types.Object) - light object + lightfilter (bpy.types.Object) - lightfilter object + + Returns: + (bpy.types.Object, bpy.types.Node) - the lightfilter object and lightfilter shader node + + ''' + rman_type = object_utils._detect_primitive_(light) + if rman_type == 'LIGHT': + light_filter_item = light.data.renderman.light_filters.add() + light_filter_item.linked_filter_ob = lightfilter + elif shadergraph_utils.is_mesh_light(light): + mat = light.active_material + if mat: + light_filter_item = mat.renderman_light.light_filters.add() + light_filter_item.linked_filter_ob = lightfilter + +def create_monkey(as_subdiv=False): + '''Create a Suzanne model + + + Arguments: + as_subdiv (bool) - whether to convert to a subdivision mesh + + Returns: + (bpy.types.Object) + + ''' + bpy.ops.mesh.primitive_monkey_add() + ob = bpy.context.object + if as_subdiv: + bpy.ops.mesh.rman_convert_subdiv() + return ob + +def create_openvdb_node(openvdb=None): + '''Import an OpenVDB file. + + Arguments: + openvdb (str) - full path to an OpeVDB file + + Returns: + (bpy.types.Object) + + ''' + if not os.path.exists(openvdb): + return None + + bpy.ops.object.rman_add_rman_geo('EXEC_DEFAULT', rman_default_name='OpenVDB', rman_prim_type='', bl_prim_type='VOLUME', filepath=openvdb, rman_convert_to_zup=True) + ob = bpy.context.object + return ob + +def create_volume_box(): + '''Create a volume box. + + Returns: + (bpy.types.Object) + + ''' + + bpy.ops.object.rman_add_rman_geo('EXEC_DEFAULT', rman_default_name='RiVolume', rman_prim_type='RI_VOLUME', bl_prim_type='') + ob = bpy.context.object + return ob + +def create_ribarchive_node(ribarchive=None): + '''Import a RIB archive file. + + Arguments: + ribarchive (str) - full path to an RIB archive file + + Returns: + (bpy.types.Object) + + ''' + if not os.path.exists(ribarchive): + return None + + bpy.ops.object.rman_add_rman_geo('EXEC_DEFAULT', rman_default_name='RIB_Archive', rman_prim_type='DELAYED_LOAD_ARCHIVE', bl_prim_type='', filepath=ribarchive, rman_convert_to_zup=False) + ob = bpy.context.object + return ob + +def create_alembic_node(alembic=None): + '''Import an alembic file (delayed). + + Arguments: + alembic (str) - full path to an Alembic file + + Returns: + (bpy.types.Object) + + ''' + if not os.path.exists(alembic): + return None + + bpy.ops.object.rman_add_rman_geo('EXEC_DEFAULT', rman_default_name='rman_AlembicArchive', rman_prim_type='ALEMBIC', bl_prim_type='', filepath=alembic, rman_convert_to_zup=True) + ob = bpy.context.object + return ob + +def create_bxdf(node_type): + '''Create a bxdf shader node and material + + + Arguments: + node_type (str) - name of the Bxdf you want to create (ex: PxrSurface) + ob (bpy.types.Object) - optional object to attach material to + + Returns: + (bpy.types.Material, bpy.types.Node) - material and bxdf node + + ''' + material = shadergraph_utils.create_bxdf(node_type) + nt = material.node_tree + output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanOutputNode') + bxdf = output.inputs[0].links[0].from_node + return (material, bxdf) + +def attach_material(material, ob): + '''Attach material to object + + + Arguments: + node_type (bpy.types.Material) - material + ob (bpy.types.Object) - object to attach material to + + Returns: + (bool) - True if material attached. False otherwise + + ''' + try: + if ob.type == 'EMPTY': + ob.renderman.rman_material_override = material + else: + ob.active_material = material + except: + return False + return True + +def create_pattern(node_type, material): + '''Create a pattern node + + + Arguments: + node_type (str) - name of the pattern node you want to create (ex: PxrChecker) + material (bpy.types.Material) - material to create the pattern node in + + Returns: + (bpy.types.Node) - pattern node + + ''' + nt = material.node_tree + pattern = nt.nodes.new('%sPatternNode' % node_type) + return pattern + +def connect_nodes(output_node, output_socket, input_node, input_socket): + '''Connect shading nodes + + Example: + import RenderManForBlender.rfb_api as rfb_api + + # create material and bxdf + material, bxdf = rfb_api.create_bxdf('PxrDisneyBsdf') + + # create PxrChecker pattern node + checker = rfb_api.create_pattern('PxrChecker', material) + checker.colorB = (0.0, 1.0, 0.4) + + # connect PxrChecker to Bxdf + rfb_api.connect_nodes(checker, 'resultRGB', bxdf, 'baseColor') + + + Arguments: + output_node (bpy.types.Node) - output node + output_socket (str) - output node socket name + input_node (bpy.types.Node) - input node + input_socket (str) - input node socket name + + Returns: + (bool) - True if connection was successful. False otherwise. + + ''' + if output_node.id_data != input_node.id_data: + rfb_log().error("%s and %s are from different node trees" % (output_node.name, input_node.name)) + return False + if output_socket not in output_node.outputs: + rfb_log().error("%s does not have output %s" % (output_node.name, output_socket)) + return False + if input_socket not in input_node.inputs: + rfb_log().error("%s does not have input %s" % (input_node.name, input_socket)) + return False + + nt = output_node.id_data + nt.links.new(output_node.outputs[output_socket], input_node.inputs[input_socket]) + + return True + +def get_node_inputs(node): + ''' + Return a list of the node's inputs' names + + Arguments: + node (bpy.types.Node) - the shading node we want to interrogate + + Returns: + (list) - the list of names of the node's inputs + + ''' + return node.inputs.keys() + +def get_node_outputs(node): + ''' + Return a list of the node's outputs' names + + Arguments: + node (bpy.types.Node) - the shading node we want to interrogate + + Returns: + (list) - the list of names of the node's outputs + + ''' + return node.outputs.keys() + def GetConfigurablePanels(): '''Return the names of RenderForBlender panels that are configurable. diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index 28522e9d..3590ca55 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -108,7 +108,7 @@ def execute(self, context): bxdf = output.inputs['bxdf_in'].links[0].from_node bxdf.densityFloatPrimVar = 'density' - if self.properties.rman_open_filebrowser: + if self.properties.rman_open_filebrowser or self.filepath != "": if rm.primitive == 'DELAYED_LOAD_ARCHIVE': ob.empty_display_type = 'CUBE' rm.path_archive = self.properties.filepath From 5fb988cf451a886c8865cff52b29468290dae076 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 27 Sep 2022 13:43:51 -0700 Subject: [PATCH 193/278] For now, only use QtCore.Qt.WindowStaysOnTopHint on the mac. --- rman_ui/rfb_qt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index b7aebe87..4916df86 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -88,7 +88,8 @@ class RmanQtWrapper(QtWidgets.QDialog): def __init__(self): super().__init__() - self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) + if sys.platform == "darwin": + self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) def closeEvent(self, event): event.accept() From c12a9c1522da0c7366d6ae3506f4a721261eff17 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 27 Sep 2022 15:20:05 -0700 Subject: [PATCH 194/278] Fix typos in the QtApp version of the trace group editor. --- .../rman_operators_editors_tracegroups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index f8686c78..0cfdb896 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -28,7 +28,7 @@ def __init__(self): super(TraceGroupsQtAppTimed, self).__init__() def execute(self, context): - self._window = TraceGroupsQtAppTimed() + self._window = TraceGroupsQtWrapper() return super(TraceGroupsQtAppTimed, self).execute(context) class StandardItem(QtGui.QStandardItem): @@ -437,7 +437,7 @@ def invoke(self, context, event): if sys.platform == "darwin": rfb_qt.run_with_timer(__TRACE_GROUPS_WINDOW__, TraceGroupsQtWrapper) else: - wm.trace_groups_qt_app_timed() + bpy.ops.wm.trace_groups_qt_app_timed() return {'RUNNING_MODAL'} From 7d2664138fcf0df5682bc382d30b32ecae5154ed Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 29 Sep 2022 12:00:37 -0700 Subject: [PATCH 195/278] For windows and linux, don't put Qt windows always on top. Just have them be the active window. --- rman_ui/rfb_qt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index 4916df86..ffee1803 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -90,6 +90,8 @@ def __init__(self): super().__init__() if sys.platform == "darwin": self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) + else: + self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) def closeEvent(self, event): event.accept() From 63eb35b4d291db768f798254af5c9ff628aef41e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 29 Sep 2022 13:12:21 -0700 Subject: [PATCH 196/278] * for now, turn off the Qt version of the trace groups window * for windows and linux, explicitly set the background color for the Qt preset browser window. For som reason, the color for the widgets are getting overblown. --- .../rman_operators_editors_tracegroups.py | 2 ++ rman_presets/qt_app.py | 8 ++++++++ rman_ui/rfb_qt.py | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 0cfdb896..ad09d1c1 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -432,6 +432,7 @@ def check_tracegroups(self, context): def invoke(self, context, event): + ''' if rfb_qt and get_pref('rman_ui_framework') == 'QT': global __TRACE_GROUPS_WINDOW__ if sys.platform == "darwin": @@ -440,6 +441,7 @@ def invoke(self, context, event): bpy.ops.wm.trace_groups_qt_app_timed() return {'RUNNING_MODAL'} + ''' wm = context.window_manager width = rfb_config['editor_preferences']['tracesets_editor']['width'] diff --git a/rman_presets/qt_app.py b/rman_presets/qt_app.py index 09178167..622da5dc 100644 --- a/rman_presets/qt_app.py +++ b/rman_presets/qt_app.py @@ -32,6 +32,14 @@ def __init__(self): self.resize(1024, 1024) self.setWindowTitle('RenderMan Preset Browser') + + if sys.platform != "darwin": + bg_role = self.backgroundRole() + plt = self.palette() + bg_color = plt.color(bg_role) + bg_color.setRgb(70, 70, 70) + plt.setColor(bg_role, bg_color) + self.setPalette(plt) self.hostPrefs = bl_pb_core.get_host_prefs() self.ui = rui.Ui(self.hostPrefs, parent=self) diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index ffee1803..0e10c705 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -91,7 +91,7 @@ def __init__(self): if sys.platform == "darwin": self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) else: - self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) def closeEvent(self, event): event.accept() From 17f4b2190e58054e981a9d147be4daf35488952b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 3 Oct 2022 07:54:13 -0700 Subject: [PATCH 197/278] Add an rman_label property to RendermanSocket class. We cannot have duplicate identifiers on sockets on the same node, so we use rman_label, when drawing the socket. --- rfb_utils/rman_socket_utils.py | 1 + rman_bl_nodes/rman_bl_nodes_sockets.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/rfb_utils/rman_socket_utils.py b/rfb_utils/rman_socket_utils.py index 5cab738c..e1bd674e 100644 --- a/rfb_utils/rman_socket_utils.py +++ b/rfb_utils/rman_socket_utils.py @@ -35,6 +35,7 @@ def node_add_input(node, param_type, param_name, meta, param_label): socket = node.inputs.new( __RMAN_SOCKET_MAP__[param_type], param_name, identifier=param_label) socket.link_limit = 1 + socket.rman_label = param_label if param_type in ['struct', 'vstruct', 'void']: socket.hide_value = True diff --git a/rman_bl_nodes/rman_bl_nodes_sockets.py b/rman_bl_nodes/rman_bl_nodes_sockets.py index 6997cf08..b6159080 100644 --- a/rman_bl_nodes/rman_bl_nodes_sockets.py +++ b/rman_bl_nodes/rman_bl_nodes_sockets.py @@ -223,10 +223,13 @@ def update_func(self, context): class RendermanSocket: ui_open: BoolProperty(name='UI Open', default=True) + rman_label: StringProperty(name="rman_label", default="") def get_pretty_name(self, node): if node.bl_idname in __CYCLES_GROUP_NODES__: return self.name + elif self.rman_label != '': + return self.rman_label else: return self.identifier From 4f421f0ae98773cb57c12948df429f57c46c45e6 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 3 Oct 2022 09:47:12 -0700 Subject: [PATCH 198/278] For RMAN-19723 * Extend the scene version string to include a patch number. This helps when we need to do upgrades in between major dot releases or beta releases. * Add an upgrade_250_1 function to upgrade all Lama nodes with the new name. --- rfb_utils/upgrade_utils.py | 112 ++++++++++++++++++++++++++++++++++-- rman_config/config/rfb.json | 2 +- rman_constants.py | 4 ++ 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index fbbb114b..11e23b18 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -104,13 +104,79 @@ def upgrade_250(scene): output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanProjectionsOutputNode') if 'Projection' in output.inputs: output.inputs['Projection'].name = 'projection_in' - + +def upgrade_250_1(scene): + ''' + Upgrade lama nodes + + 25.0b2 changed the names of input parameters for lama nodes, + because they were using OSL reserved keywords (ex: color) + ''' + + def copy_param(old_node, new_node, old_nm, new_nm): + if old_node.inputs[old_nm].is_linked: + connected_socket = old_node.inputs[old_nm].links[0].from_socket + nt.links.new(connected_socket, new_node.inputs[new_nm]) + else: + setattr(new_node, new_nm, getattr(n, old_nm)) + + for mat in bpy.data.materials: + if mat.node_tree is None: + continue + nt = mat.node_tree + nodes = [n for n in nt.nodes] + for n in nodes: + new_node = None + if n.bl_label == 'LamaDiffuse': + new_node = nt.nodes.new('LamaDiffuseBxdfNode') + copy_param(n, new_node, 'color', 'diffuseColor') + copy_param(n, new_node, 'normal', 'diffuseNormal') + elif n.bl_label == 'LamaSheen': + new_node = nt.nodes.new('LamaSheenBxdfNode') + copy_param(n, new_node, 'color', 'sheenColor') + copy_param(n, new_node, 'normal', 'sheenNormal') + elif n.bl_label == 'LamaConductor': + new_node = nt.nodes.new('LamaConductorBxdfNode') + copy_param(n, new_node, 'normal', 'conductorNormal') + elif n.bl_label == 'LamaDielectric': + new_node = nt.nodes.new('LamaDielectricBxdfNode') + copy_param(n, new_node, 'normal', 'dielectricNormal') + elif n.bl_label == 'LamaEmission': + new_node = nt.nodes.new('LamaEmissionBxdfNode') + copy_param(n, new_node, 'color', 'emissionColor') + elif n.bl_label == 'LamaGeneralizedSchlick': + new_node = nt.nodes.new('LamaGeneralizedSchlickBxdfNode') + copy_param(n, new_node, 'normal', 'genSchlickNormal') + elif n.bl_label == 'LamaSSS': + new_node = nt.nodes.new('LamaSSSBxdfNode') + copy_param(n, new_node, 'color', 'sssColor') + copy_param(n, new_node, 'normal', 'sssNormal') + elif n.bl_label == 'LamaTranslucent': + new_node = nt.nodes.new('LamaTranslucentBxdfNode') + copy_param(n, new_node, 'color', 'translucentColor') + copy_param(n, new_node, 'normal', 'translucentNormal') + elif n.bl_label == 'LamaTricolorSSS': + new_node = nt.nodes.new('LamaTricolorSSSBxdfNode') + copy_param(n, new_node, 'normal', 'sssNormal') + + if new_node: + new_node.location[0] = n.location[0] + new_node.location[1] = n.location[1] + if n.outputs['bxdf_out'].is_linked: + for link in n.outputs['bxdf_out'].links: + connected_socket = link.to_socket + nt.links.new(new_node.outputs['bxdf_out'], connected_socket) + node_name = n.name + nt.nodes.remove(n) + new_node.name = node_name + new_node.select = False __RMAN_SCENE_UPGRADE_FUNCTIONS__ = OrderedDict() __RMAN_SCENE_UPGRADE_FUNCTIONS__['24.2'] = upgrade_242 __RMAN_SCENE_UPGRADE_FUNCTIONS__['24.3'] = upgrade_243 __RMAN_SCENE_UPGRADE_FUNCTIONS__['25.0'] = upgrade_250 +__RMAN_SCENE_UPGRADE_FUNCTIONS__['25.0.1'] = upgrade_250_1 def upgrade_scene(bl_scene): global __RMAN_SCENE_UPGRADE_FUNCTIONS__ @@ -123,15 +189,53 @@ def upgrade_scene(bl_scene): # we started adding a renderman_version property in 24.1 version = '24.1' + scene_major = None + scene_minor = None + scene_patch = None + tokens = version.split('.') + scene_major = tokens[0] + scene_minor = tokens[1] + if len(tokens) > 2: + scene_patch = tokens[2] + for version_str, fn in __RMAN_SCENE_UPGRADE_FUNCTIONS__.items(): - if version < version_str: + upgrade_major = None + upgrade_minor = None + upgrade_patch = None + tokens = version_str.split('.') + upgrade_major = tokens[0] + upgrade_minor = tokens[1] + if len(tokens) > 2: + upgrade_patch = tokens[2] + + if scene_major < upgrade_major: + rfb_log().debug('Upgrade scene to %s' % version_str) + fn(scene) + continue + + if scene_major == upgrade_major and scene_minor < upgrade_minor: rfb_log().debug('Upgrade scene to %s' % version_str) fn(scene) + continue + + if not scene_patch and not upgrade_patch: + continue - scene.renderman.renderman_version = rman_constants.RMAN_SUPPORTED_VERSION_STRING + if not scene_patch and upgrade_patch: + # The scene version doesn't include a patch version + # This is probably from an older version i.e.: < 25.0b1 + rfb_log().debug('Upgrade scene to %s' % version_str) + fn(scene) + continue + + if scene_patch < upgrade_patch: + rfb_log().debug('Upgrade scene to %s' % version_str) + fn(scene) + + scene.renderman.renderman_version = rman_constants.RFB_SCENE_VERSION_STRING def update_version(bl_scene): if bpy.context.engine != 'PRMAN_RENDER': return for scene in bpy.data.scenes: - scene.renderman.renderman_version = rman_constants.RMAN_SUPPORTED_VERSION_STRING \ No newline at end of file + scene.renderman.renderman_version = rman_constants.RFB_SCENE_VERSION_STRING \ No newline at end of file diff --git a/rman_config/config/rfb.json b/rman_config/config/rfb.json index bc1897c5..299ad374 100644 --- a/rman_config/config/rfb.json +++ b/rman_config/config/rfb.json @@ -81,7 +81,7 @@ "diffuse_color": ["diffuseColor"] }, "LamaDiffuse": { - "diffuse_color": ["color"] + "diffuse_color": ["diffuseColor"] } }, "disabled_nodes": [ diff --git a/rman_constants.py b/rman_constants.py index 918fcc51..67693d8a 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -31,6 +31,10 @@ RMAN_SUPPORTED_VERSION = (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR, RMAN_SUPPORTED_VERSION_BETA) RMAN_SUPPORTED_VERSION_STRING = '%d.%d%s' % (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR, RMAN_SUPPORTED_VERSION_BETA) +RFB_SCENE_VERSION_MAJOR = RMAN_SUPPORTED_VERSION_MAJOR +RFB_SCENE_VERSION_MINOR = RMAN_SUPPORTED_VERSION_MINOR +RFB_SCENE_VERSION_PATCH = 0 +RFB_SCENE_VERSION_STRING = '%d.%d.%d' % (RFB_SCENE_VERSION_MAJOR, RFB_SCENE_VERSION_MINOR, RFB_SCENE_VERSION_PATCH) RFB_ADDON_DESCRIPTION = 'RenderMan %d.%d integration' % (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR) From 0f7a515b39ed4f611a0f86286c72dde5d9098215 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 3 Oct 2022 12:04:26 -0700 Subject: [PATCH 199/278] Pass a function pointer for help_func to TxManagerUI. Also, move our help URL to rman_constants.py. --- rman_constants.py | 2 ++ rman_ui/rman_ui_txmanager_qt.py | 8 ++++++-- rman_ui/rman_ui_view3d_panels.py | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/rman_constants.py b/rman_constants.py index 67693d8a..f3856fd4 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -46,6 +46,8 @@ RMAN_RENDERMAN_BLUE = (0.0, 0.498, 1.0, 1.0) RMAN_FAKE_NODEGROUP = '.__RMAN_FAKE_NODEGROUP__' +RFB_HELP_URL = "https://rmanwiki.pixar.com/display/RFB%s" % RMAN_SUPPORTED_VERSION_MAJOR + RFB_FLOAT3 = ['color', 'point', 'vector', 'normal'] RFB_FLOATX = ['color', 'point', 'vector', 'normal', 'matrix'] diff --git a/rman_ui/rman_ui_txmanager_qt.py b/rman_ui/rman_ui_txmanager_qt.py index 0f2c45e5..620b58d2 100644 --- a/rman_ui/rman_ui_txmanager_qt.py +++ b/rman_ui/rman_ui_txmanager_qt.py @@ -10,6 +10,7 @@ import hashlib from ..rfb_logger import rfb_log +from ..rman_constants import RFB_HELP_URL __QT_LOADED__ = True __TXMANAGER_WINDOW__ = None @@ -45,7 +46,10 @@ def _append_to_tx_list(file_path_list): txmgr.add_texture(texid, fpath) txmgr.update_ui_list() # make sure to restart the queue. - txmgr.txmake_all(start_queue=True, blocking=False) + txmgr.txmake_all(start_queue=True, blocking=False) + +def help_func(url): + bpy.ops.wm.url_open(url = RFB_HELP_URL) def create_widget(): global __TXMANAGER_WINDOW__ @@ -56,7 +60,7 @@ def create_widget(): __TXMANAGER_WINDOW__ = rui.TxManagerUI(None, txmanager=mgr, parse_scene_func=parse_scene, append_tx_func=_append_to_tx_list, - help_func=None) + help_func=help_func) mgr.ui = __TXMANAGER_WINDOW__ return __TXMANAGER_WINDOW__ diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index 45103109..1371114c 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -5,6 +5,7 @@ from ..rfb_logger import rfb_log from .rman_ui_base import _RManPanelHeader from ..rman_render import RmanRender +from ..rman_constants import RFB_HELP_URL import bpy class PRMAN_PT_Renderman_UI_Panel(bpy.types.Panel, _RManPanelHeader): @@ -248,7 +249,7 @@ def draw(self, context): layout.label(text="Help:") rman_help = rfb_icons.get_icon("rman_help") layout.operator("wm.url_open", text="RenderMan Docs", - icon_value=rman_help.icon_id).url = "https://rmanwiki.pixar.com/display/RFB24" + icon_value=rman_help.icon_id).url = RFB_HELP_URL rman_info = rfb_icons.get_icon("rman_blender") layout.operator("renderman.about_renderman", icon_value=rman_info.icon_id) From 3057e80e8c1b2ae47a5299b28036742a35c95d33 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 3 Oct 2022 16:23:08 -0700 Subject: [PATCH 200/278] Initial support for the new hair curves object in Blender 3.3. Not sure how to get the UVs from the underlying mesh, so we currently don't have a scalpST. --- rman_scene.py | 2 + rman_sg_nodes/rman_sg_haircurves.py | 8 + .../rman_hair_curves_translator.py | 141 ++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 rman_sg_nodes/rman_sg_haircurves.py create mode 100644 rman_translators/rman_hair_curves_translator.py diff --git a/rman_scene.py b/rman_scene.py index bded6364..f7ad0f44 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -23,6 +23,7 @@ from .rman_translators.rman_emitter_translator import RmanEmitterTranslator from .rman_translators.rman_empty_translator import RmanEmptyTranslator from .rman_translators.rman_alembic_translator import RmanAlembicTranslator +from .rman_translators.rman_hair_curves_translator import RmanHairCurvesTranslator # utils from .rfb_utils import object_utils @@ -168,6 +169,7 @@ def create_translators(self): self.rman_translators['RI_VOLUME'] = RmanVolumeTranslator(rman_scene=self) self.rman_translators['BRICKMAP'] = RmanBrickmapTranslator(rman_scene=self) self.rman_translators['ALEMBIC'] = RmanAlembicTranslator(rman_scene=self) + self.rman_translators['CURVES'] = RmanHairCurvesTranslator(rman_scene=self) def _find_renderman_layer(self): self.rm_rl = None diff --git a/rman_sg_nodes/rman_sg_haircurves.py b/rman_sg_nodes/rman_sg_haircurves.py new file mode 100644 index 00000000..9b73554a --- /dev/null +++ b/rman_sg_nodes/rman_sg_haircurves.py @@ -0,0 +1,8 @@ +from .rman_sg_node import RmanSgNode + +class RmanSgHairCurves(RmanSgNode): + + def __init__(self, rman_scene, sg_node, db_name): + super().__init__(rman_scene, sg_node, db_name) + + self.sg_curves_list = list() diff --git a/rman_translators/rman_hair_curves_translator.py b/rman_translators/rman_hair_curves_translator.py new file mode 100644 index 00000000..eedfd5ff --- /dev/null +++ b/rman_translators/rman_hair_curves_translator.py @@ -0,0 +1,141 @@ +from .rman_translator import RmanTranslator +from ..rfb_utils import transform_utils +from ..rfb_utils import scenegraph_utils +from ..rfb_logger import rfb_log +from ..rman_sg_nodes.rman_sg_haircurves import RmanSgHairCurves +from mathutils import Vector +import math +import bpy +import numpy as np + +class BlHair: + + def __init__(self): + self.points = [] + self.next_points = [] + self.vertsArray = [] + self.scalpST = [] + self.mcols = [] + self.nverts = 0 + self.hair_width = [] + self.index = [] + + @property + def constant_width(self): + return (len(self.hair_width) < 2) + +class RmanHairCurvesTranslator(RmanTranslator): + + def __init__(self, rman_scene): + super().__init__(rman_scene) + self.bl_type = 'CURVES' + + def export(self, ob, db_name): + + sg_node = self.rman_scene.sg_scene.CreateGroup(db_name) + rman_sg_hair = RmanSgHairCurves(self.rman_scene, sg_node, db_name) + + return rman_sg_hair + + def clear_children(self, ob, rman_sg_hair): + if rman_sg_hair.sg_node: + for c in [ rman_sg_hair.sg_node.GetChild(i) for i in range(0, rman_sg_hair.sg_node.GetNumChildren())]: + rman_sg_hair.sg_node.RemoveChild(c) + self.rman_scene.sg_scene.DeleteDagNode(c) + rman_sg_hair.sg_curves_list.clear() + + def export_deform_sample(self, rman_sg_hair, ob, time_sample): + curves = self._get_strands_(ob) + for i, bl_curve in enumerate(curves): + curves_sg = rman_sg_hair.sg_curves_list[i] + if not curves_sg: + continue + primvar = curves_sg.GetPrimVars() + + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.points, "vertex", time_sample) + curves_sg.SetPrimVars(primvar) + + def update(self, ob, rman_sg_hair): + if rman_sg_hair.sg_node: + if rman_sg_hair.sg_node.GetNumChildren() > 0: + self.clear_children(ob, rman_sg_hair) + + curves = self._get_strands_(ob) + if not curves: + return + + for i, bl_curve in enumerate(curves): + curves_sg = self.rman_scene.sg_scene.CreateCurves("%s-%d" % (rman_sg_hair.db_name, i)) + curves_sg.Define(self.rman_scene.rman.Tokens.Rix.k_cubic, "nonperiodic", "catmull-rom", len(bl_curve.vertsArray), len(bl_curve.points)) + primvar = curves_sg.GetPrimVars() + primvar.SetPointDetail(self.rman_scene.rman.Tokens.Rix.k_P, bl_curve.points, "vertex") + + primvar.SetIntegerDetail(self.rman_scene.rman.Tokens.Rix.k_Ri_nvertices, bl_curve.vertsArray, "uniform") + index_nm = 'index' + primvar.SetIntegerDetail(index_nm, bl_curve.index, "uniform") + + width_detail = "vertex" + primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, bl_curve.hair_width, width_detail) + + ''' + if len(bl_curve.scalpST): + primvar.SetFloatArrayDetail("scalpST", bl_curve.scalpST, 2, "uniform") + + if len(bl_curve.mcols): + primvar.SetColorDetail("Cs", bl_curve.mcols, "uniform") + ''' + + curves_sg.SetPrimVars(primvar) + rman_sg_hair.sg_node.AddChild(curves_sg) + rman_sg_hair.sg_curves_list.append(curves_sg) + + def add_object_instance(self, rman_sg_hair, rman_sg_group): + rman_sg_hair.sg_node.AddChild(rman_sg_group.sg_node) + rman_sg_hair.instances[rman_sg_group.db_name] = rman_sg_group + rman_sg_group.rman_sg_group_parent = rman_sg_hair + + def _get_strands_(self, ob): + + curve_sets = [] + bl_curve = BlHair() + db = ob.data + for curve in db.curves: + if curve.points_length < 4: + continue + + npoints = len(curve.points) + strand_points = np.zeros(npoints*3, dtype=np.float32) + widths = np.zeros(npoints, dtype=np.float32) + curve.points.foreach_get('position', strand_points) + curve.points.foreach_get('radius', widths) + strand_points = np.reshape(strand_points, (npoints, 3)) + widths = widths * 2 + strand_points = strand_points.tolist() + widths = widths.tolist() + + ''' + # do we really need to double the end points? + strand_points = strand_points[:1] + \ + strand_points + strand_points[-1:] + + widths = widths[:1] + widths + widths[-1:] + ''' + vertsInStrand = len(strand_points) + + bl_curve.points.extend(strand_points) + bl_curve.vertsArray.append(vertsInStrand) + bl_curve.hair_width.extend(widths) + bl_curve.index.append(curve.index) + bl_curve.nverts += vertsInStrand + + # if we get more than 100000 vertices, start a new BlHair. This + # is to avoid a maxint on the array length + if bl_curve.nverts > 100000: + curve_sets.append(bl_curve) + bl_curve = BlHair() + + if bl_curve.nverts > 0: + curve_sets.append(bl_curve) + + return curve_sets + \ No newline at end of file From 654e2bce186b13fcfdba478278a5389962c9ab49 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Oct 2022 11:14:02 -0700 Subject: [PATCH 201/278] Add support for emitting hair curves attributes as primvars. --- .../rman_hair_curves_translator.py | 106 ++++++++++++++++-- 1 file changed, 95 insertions(+), 11 deletions(-) diff --git a/rman_translators/rman_hair_curves_translator.py b/rman_translators/rman_hair_curves_translator.py index eedfd5ff..efd0f028 100644 --- a/rman_translators/rman_hair_curves_translator.py +++ b/rman_translators/rman_hair_curves_translator.py @@ -19,10 +19,15 @@ def __init__(self): self.nverts = 0 self.hair_width = [] self.index = [] + self.bl_hair_attributes = [] - @property - def constant_width(self): - return (len(self.hair_width) < 2) +class BlHairAttribute: + + def __init__(self): + self.rman_type = '' + self.rman_name = '' + self.rman_detail = None + self.values = [] class RmanHairCurvesTranslator(RmanTranslator): @@ -77,13 +82,19 @@ def update(self, ob, rman_sg_hair): width_detail = "vertex" primvar.SetFloatDetail(self.rman_scene.rman.Tokens.Rix.k_width, bl_curve.hair_width, width_detail) - ''' - if len(bl_curve.scalpST): - primvar.SetFloatArrayDetail("scalpST", bl_curve.scalpST, 2, "uniform") - - if len(bl_curve.mcols): - primvar.SetColorDetail("Cs", bl_curve.mcols, "uniform") - ''' + for hair_attr in bl_curve.bl_hair_attributes: + if hair_attr.rman_detail is None: + continue + if hair_attr.rman_type == "float": + primvar.SetFloatDetail(hair_attr.rman_name, hair_attr.values, hair_attr.rman_detail) + elif hair_attr.rman_type == "float2": + primvar.SetFloatArrayDetail(hair_attr.rman_name, hair_attr.values, 2, hair_attr.rman_detail) + elif hair_attr.rman_type == "vector": + primvar.SetVectorDetail(hair_attr.rman_name, hair_attr.values, hair_attr.rman_detail) + elif hair_attr.rman_type == 'color': + primvar.SetColorDetail(hair_attr.rman_name, hair_attr.values, hair_attr.rman_detail) + elif hair_attr.rman_type == 'integer': + primvar.SetIntegerDetail(hair_attr.rman_name, hair_attr.values, hair_attr.rman_detail) curves_sg.SetPrimVars(primvar) rman_sg_hair.sg_node.AddChild(curves_sg) @@ -94,6 +105,74 @@ def add_object_instance(self, rman_sg_hair, rman_sg_group): rman_sg_hair.instances[rman_sg_group.db_name] = rman_sg_group rman_sg_group.rman_sg_group_parent = rman_sg_hair + def get_attributes(self, ob, bl_curve): + for attr in ob.data.attributes: + if attr.name in ['position']: + continue + hair_attr = None + if attr.data_type == 'FLOAT2': + hair_attr = BlHairAttribute() + hair_attr.rman_name = attr.name + hair_attr.rman_type = 'float2' + + npoints = len(attr.data) + values = np.zeros(npoints*2, dtype=np.float32) + attr.data.foreach_get('vector', values) + values = np.reshape(values, (npoints, 2)) + hair_attr.values = values.tolist() + + elif attr.data_type == 'FLOAT_VECTOR': + hair_attr = BlHairAttribute() + hair_attr.rman_name = attr.name + hair_attr.rman_type = 'vector' + + npoints = len(attr.data) + values = np.zeros(npoints*3, dtype=np.float32) + attr.data.foreach_get('vector', values) + values = np.reshape(values, (npoints, 3)) + hair_attr.values = values.tolist() + + elif attr.data_type in ['BYTE_COLOR', 'FLOAT_COLOR']: + hair_attr = BlHairAttribute() + hair_attr.rman_name = attr.name + if attr.name == 'color': + hair_attr.rman_name = 'Cs' + hair_attr.rman_type = 'color' + + npoints = len(attr.data) + values = np.zeros(npoints*4, dtype=np.float32) + attr.data.foreach_get('color', values) + values = np.reshape(values, (npoints, 4)) + hair_attr.values .extend(values[0:, 0:3].tolist()) + + elif attr.data_type == 'FLOAT': + hair_attr = BlHairAttribute() + hair_attr.rman_name = attr.name + hair_attr.rman_type = 'float' + hair_attr.array_len = -1 + + npoints = len(attr.data) + values = np.zeros(npoints, dtype=np.float32) + attr.data.foreach_get('value', values) + hair_attr.values = values.tolist() + elif attr.data_type in ['INT8', 'INT']: + hair_attr = BlHairAttribute() + hair_attr.rman_name = attr.name + hair_attr.rman_type = 'integer' + hair_attr.array_len = -1 + + npoints = len(attr.data) + values = np.zeros(npoints, dtype=np.int) + attr.data.foreach_get('value', values) + hair_attr.values = values.tolist() + + if hair_attr: + bl_curve.bl_hair_attributes.append(hair_attr) + if len(attr.data) == len(ob.data.curves): + hair_attr.rman_detail = 'uniform' + elif len(attr.data) == len(ob.data.points): + hair_attr.rman_detail = 'vertex' + def _get_strands_(self, ob): curve_sets = [] @@ -109,6 +188,9 @@ def _get_strands_(self, ob): curve.points.foreach_get('position', strand_points) curve.points.foreach_get('radius', widths) strand_points = np.reshape(strand_points, (npoints, 3)) + if np.count_nonzero(widths) == 0: + # radius is 0. Default to 0.005 + widths.fill(0.005) widths = widths * 2 strand_points = strand_points.tolist() widths = widths.tolist() @@ -126,15 +208,17 @@ def _get_strands_(self, ob): bl_curve.vertsArray.append(vertsInStrand) bl_curve.hair_width.extend(widths) bl_curve.index.append(curve.index) - bl_curve.nverts += vertsInStrand + bl_curve.nverts += vertsInStrand # if we get more than 100000 vertices, start a new BlHair. This # is to avoid a maxint on the array length if bl_curve.nverts > 100000: + self.get_attributes(ob, bl_curve) curve_sets.append(bl_curve) bl_curve = BlHair() if bl_curve.nverts > 0: + self.get_attributes(ob, bl_curve) curve_sets.append(bl_curve) return curve_sets From b212957fcbf0bf6f497fdd888b3804b71287d632 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 6 Oct 2022 09:37:08 -0700 Subject: [PATCH 202/278] Not really sure if curves node supports deformation motion blur, but just get the translator ready to support it, if they do. --- rman_scene.py | 2 +- rman_translators/rman_hair_curves_translator.py | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/rman_scene.py b/rman_scene.py index f7ad0f44..a339bda6 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -914,7 +914,7 @@ def export_instances_motion(self, selected_objects=False): # deformation blur if rman_sg_node.is_deforming and seg in rman_sg_node.deform_motion_steps: rman_type = rman_sg_node.rman_type - if rman_type in ['MESH', 'FLUID']: + if rman_type in ['MESH', 'FLUID', 'CURVES']: translator = self.rman_translators.get(rman_type, None) if translator: deform_idx = 0 diff --git a/rman_translators/rman_hair_curves_translator.py b/rman_translators/rman_hair_curves_translator.py index efd0f028..e3ec8bdc 100644 --- a/rman_translators/rman_hair_curves_translator.py +++ b/rman_translators/rman_hair_curves_translator.py @@ -12,10 +12,7 @@ class BlHair: def __init__(self): self.points = [] - self.next_points = [] self.vertsArray = [] - self.scalpST = [] - self.mcols = [] self.nverts = 0 self.hair_width = [] self.index = [] @@ -100,11 +97,6 @@ def update(self, ob, rman_sg_hair): rman_sg_hair.sg_node.AddChild(curves_sg) rman_sg_hair.sg_curves_list.append(curves_sg) - def add_object_instance(self, rman_sg_hair, rman_sg_group): - rman_sg_hair.sg_node.AddChild(rman_sg_group.sg_node) - rman_sg_hair.instances[rman_sg_group.db_name] = rman_sg_group - rman_sg_group.rman_sg_group_parent = rman_sg_hair - def get_attributes(self, ob, bl_curve): for attr in ob.data.attributes: if attr.name in ['position']: From 9a42eb16e3cc25e5f1c6b655f6a7aa6b7870ebea Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 6 Oct 2022 12:51:45 -0700 Subject: [PATCH 203/278] Use the new register_classes functions in rman_engine --- rman_engine.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/rman_engine.py b/rman_engine.py index e88e7978..1fdee647 100644 --- a/rman_engine.py +++ b/rman_engine.py @@ -4,6 +4,7 @@ from .rfb_utils.prefs_utils import get_pref from .rfb_utils import string_utils +from .rfb_utils import register_utils from .rfb_logger import rfb_log class PRManRender(bpy.types.RenderEngine): @@ -238,13 +239,7 @@ def _draw_pixels(self, context, depsgraph): ] def register(): - for cls in classes: - bpy.utils.register_class(cls) + register_utils.rman_register_classes(classes) def unregister(): - for cls in classes: - try: - bpy.utils.unregister_class(cls) - except RuntimeError: - rfb_log().debug('Could not unregister class: %s' % str(cls)) - pass \ No newline at end of file + register_utils.rman_unregister_classes(classes) \ No newline at end of file From 85b0e9b562ec7a3cbfbd01ece75edaa90ff798e7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 11 Oct 2022 18:40:02 -0700 Subject: [PATCH 204/278] Add a preference to be able to change the zoom factor for the Enhance operator. --- preferences.py | 12 +++++++++++- rman_ui/rman_ui_viewport.py | 15 +++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/preferences.py b/preferences.py index 5bfaebd2..a7300608 100644 --- a/preferences.py +++ b/preferences.py @@ -90,7 +90,8 @@ 'rman_roz_grpcServer': True, 'rman_roz_webSocketServer': False, 'rman_roz_webSocketServer_Port': 0, - 'rman_roz_stats_print_level': '1' + 'rman_roz_stats_print_level': '1', + 'rman_enhance_zoom_factor': 5 } class RendermanPreferencePath(bpy.types.PropertyGroup): @@ -523,6 +524,14 @@ def update_stats_config(self, context): ], description="How much live stats to print", update=update_stats_config + ) + + rman_enhance_zoom_factor: IntProperty( + name="Enhance Zoom Factor", + description="How much to zoom in when using the Enhance operator", + default=5, + min=2, + max=10 ) def draw_xpu_devices(self, context, layout): @@ -586,6 +595,7 @@ def draw(self, context): col.prop(self, 'rman_solo_collapse_nodes') col.prop(self, 'rman_use_blend_dir_token') col.prop(self, 'rman_editor') + col.prop(self, 'rman_enhance_zoom_factor') # XPU Prefs if sys.platform != ("darwin") and envconfig_utils.envconfig().has_xpu_license: diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index a89025dd..8bf5ece4 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -419,8 +419,6 @@ class PRMAN_OT_Viewport_Enhance(bpy.types.Operator): bl_description = "Enhance" bl_options = {"INTERNAL"} - zoom_factor: FloatProperty(name="Zoom", default=5.0, min=1.0, max=5.0) - def __init__(self): self.x = -1 self.y = -1 @@ -438,8 +436,9 @@ def poll(cls, context): @classmethod def description(cls, context, properties): help = "NOTE: This only works with perspective cameras or the PxrCamera projection plugin.\n\n" - help += "Embiggens the region around a pixel (X,Y) by zoom" - help += "\nfactor for trouble-shooting. The magnified pixel will remain" + help += "Embiggens the region around a pixel (X,Y) by a zoom" + help += "\nfactor for trouble-shooting. The zoom factor can be changed" + help += "in the preferences. The magnified pixel will remain" help += "\nanchored in place relative to the image. Camera effects such as" help += "\nvignetting will be scaled accordingly. Intentionally does not" help += "\naffect level-of-detail, dicing, displacement, or MIP map levels." @@ -447,10 +446,13 @@ def description(cls, context, properties): help += "\n\nEnter to simply exit out of the operator, and keep the current zoom. Esc to exit and reset the zoom." return help + def get_zoom_factor(self): + zoom_factor = float(get_pref('rman_enhance_zoom_factor')) + return zoom_factor def execute(self, context): rman_render = RmanRender.get_rman_render() - rman_render.rman_scene_sync.update_enhance(context, self.x, self.y, self.zoom_factor) + rman_render.rman_scene_sync.update_enhance(context, self.x, self.y, self.get_zoom_factor()) return {'RUNNING_MODAL'} @@ -460,7 +462,7 @@ def reset(self, context): def call_upate(self, context, x, y): rman_render = RmanRender.get_rman_render() - rman_render.rman_scene_sync.update_enhance(context, x, y, self.zoom_factor) + rman_render.rman_scene_sync.update_enhance(context, x, y, self.get_zoom_factor()) def modal(self, context, event): x = event.mouse_region_x @@ -884,6 +886,7 @@ def draw(self, context): col.prop(prefs, 'rman_viewport_draw_progress') if prefs.rman_viewport_draw_progress: col.prop(prefs, 'rman_viewport_progress_color') + col.prop(prefs, 'rman_enhance_zoom_factor') if rm.current_platform != ("macOS"): col = layout.column(align=True) col.prop(rm, 'blender_ipr_optix_denoiser') From bb63faf668d700752d5837a8451a5921f233bea6 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 11 Oct 2022 18:46:25 -0700 Subject: [PATCH 205/278] In rman_register_class, check if the class is already registered. If it is, try to unregister it. --- rfb_utils/register_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rfb_utils/register_utils.py b/rfb_utils/register_utils.py index 7d879e0e..a757f2ac 100644 --- a/rfb_utils/register_utils.py +++ b/rfb_utils/register_utils.py @@ -3,6 +3,8 @@ def rman_register_class(cls): try: + if hasattr(bpy.types, str(cls)): + rman_unregister_class(cls) bpy.utils.register_class(cls) except ValueError as e: rfb_log().debug("Could not register class, %s, because: %s" % (str(cls), str(e))) From 3dfd6aafd980fd72dcd31ce4f4fcc3cb432a781d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 12 Oct 2022 13:13:43 -0700 Subject: [PATCH 206/278] For some reason the Qt stats window would not update if the stats manager was already connected before opening the Window. If we are in this case, we kick the UI's connectedTimer. --- rman_stats/operators.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rman_stats/operators.py b/rman_stats/operators.py index 78065af3..2263b9b1 100644 --- a/rman_stats/operators.py +++ b/rman_stats/operators.py @@ -37,7 +37,20 @@ def __init__(self): mgr = rr.stats_mgr.mgr self.ui = rui.StatsManagerUI(self, manager=mgr, show_connect=True, show_config=False) self.setLayout(self.ui.topLayout) - self.show() # Show window + self.show() # Show window + + def show(self): + if not self.ui.manager.clientConnected(): + self.ui.attachCB() + else: + # This is a bit weird. If the stats manager is already + # connected, the UI doesn't seem to update the connection status when + # first showing the window. + # For now, just kick the UI's connectedTimer + self.ui.connectedTimer.start(1000) + self.ui.attachBtn.setText("Connecting...") + + super(RmanStatsWrapper, self).show() def closeEvent(self, event): event.accept() From 1f94fd2d971da3eb5bab9364739e7c765bb42a99 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 12 Oct 2022 15:57:58 -0700 Subject: [PATCH 207/278] Make sure there's only one instance of our Qt apps. --- rman_presets/qt_app.py | 9 +++++++-- rman_stats/operators.py | 9 +++++++-- rman_ui/rfb_qt.py | 3 ++- rman_ui/rman_ui_txmanager_qt.py | 3 +++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/rman_presets/qt_app.py b/rman_presets/qt_app.py index c5a6b481..f2c13fed 100644 --- a/rman_presets/qt_app.py +++ b/rman_presets/qt_app.py @@ -19,7 +19,9 @@ def __init__(self): super(PresetBrowserQtAppTimed, self).__init__() def execute(self, context): - self._window = PresetBrowserWrapper() + global __PRESET_BROWSER_WINDOW__ + __PRESET_BROWSER_WINDOW__ = PresetBrowserWrapper() + self._window = __PRESET_BROWSER_WINDOW__ return super(PresetBrowserQtAppTimed, self).execute(context) class PresetBrowserWrapper(rfb_qt.RmanQtWrapper): @@ -57,8 +59,11 @@ class PRMAN_OT_Renderman_Presets_Editor(bpy.types.Operator): def execute(self, context): global __PRESET_BROWSER_WINDOW__ + if __PRESET_BROWSER_WINDOW__ and __PRESET_BROWSER_WINDOW__.isVisible(): + return {'FINISHED'} + if sys.platform == "darwin": - rfb_qt.run_with_timer(__PRESET_BROWSER_WINDOW__, PresetBrowserWrapper) + __PRESET_BROWSER_WINDOW__ = rfb_qt.run_with_timer(__PRESET_BROWSER_WINDOW__, PresetBrowserWrapper) else: bpy.ops.wm.rpb_qt_app_timed() diff --git a/rman_stats/operators.py b/rman_stats/operators.py index 2263b9b1..53630c1d 100644 --- a/rman_stats/operators.py +++ b/rman_stats/operators.py @@ -18,7 +18,9 @@ def __init__(self): super(LiveStatsQtAppTimed, self).__init__() def execute(self, context): - self._window = RmanStatsWrapper() + global __STATS_WINDOW__ + __STATS_WINDOW__ = RmanStatsWrapper() + self._window = __STATS_WINDOW__ return super(LiveStatsQtAppTimed, self).execute(context) class RmanStatsWrapper(rfb_qt.RmanQtWrapper): @@ -62,8 +64,11 @@ class PRMAN_OT_Open_Stats(bpy.types.Operator): def execute(self, context): global __STATS_WINDOW__ + if __STATS_WINDOW__ and __STATS_WINDOW__.isVisible(): + return {'FINISHED'} + if sys.platform == "darwin": - rfb_qt.run_with_timer(__STATS_WINDOW__, RmanStatsWrapper) + __STATS_WINDOW__ = rfb_qt.run_with_timer(__STATS_WINDOW__, RmanStatsWrapper) else: bpy.ops.wm.live_stats_qt_app_timed() diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index 0e10c705..48f4f103 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -110,4 +110,5 @@ def run_with_timer(window, cls): if not window: window = cls() window.show() - bpy.app.timers.register(functools.partial(process_qt_events, app, window)) \ No newline at end of file + bpy.app.timers.register(functools.partial(process_qt_events, app, window)) + return window \ No newline at end of file diff --git a/rman_ui/rman_ui_txmanager_qt.py b/rman_ui/rman_ui_txmanager_qt.py index 62affa52..a4a39918 100644 --- a/rman_ui/rman_ui_txmanager_qt.py +++ b/rman_ui/rman_ui_txmanager_qt.py @@ -73,6 +73,9 @@ class PRMAN_OT_TxManager_Qt(bpy.types.Operator): def execute(self, context): from ..rfb_utils import texture_utils global __TXMANAGER_WINDOW__ + if __TXMANAGER_WINDOW__ and __TXMANAGER_WINDOW__.isVisible(): + return {'FINISHED'} + if sys.platform == "darwin": rfb_qt.run_with_timer(__TXMANAGER_WINDOW__, create_widget) else: From b54ba9c5547e0bd56b7aa2fa8b8fec9a30aff473 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 13 Oct 2022 07:51:44 -0700 Subject: [PATCH 208/278] Typo in upgrade_scene. We need to check if the upgrade function has a patch string before doing a comparison with the scene patch string. --- rfb_utils/upgrade_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index 11e23b18..09543afc 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -228,7 +228,7 @@ def upgrade_scene(bl_scene): fn(scene) continue - if scene_patch < upgrade_patch: + if upgrade_patch and scene_patch < upgrade_patch: rfb_log().debug('Upgrade scene to %s' % version_str) fn(scene) From 878a3ff6521ae675158b765337624eb40f0d49b0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 13 Oct 2022 08:57:44 -0700 Subject: [PATCH 209/278] get_token_blender_file_path would sometimes fail to substitute paths with the token. --- rfb_utils/filepath_utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rfb_utils/filepath_utils.py b/rfb_utils/filepath_utils.py index bb9e4f95..956da9e0 100644 --- a/rfb_utils/filepath_utils.py +++ b/rfb_utils/filepath_utils.py @@ -6,6 +6,7 @@ import re from ..rfb_logger import rfb_log from .prefs_utils import get_pref +from . import string_utils def view_file(file_path): @@ -74,8 +75,12 @@ def get_token_blender_file_path(p): regex = r"^//" pout = re.sub(regex, '/', p, 0, re.MULTILINE) else: - pout = p - + blend_dir = string_utils.get_var('blend_dir') + if blend_dir.endswith('/'): + pout = p.replace(blend_dir, '') + else: + pout = p.replace(blend_dir, '/') + return pout.replace('\\', '/') def filesystem_path(p): From f80bdc494b278235a8d7ff06184644d56ffd2559 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 8 Nov 2022 10:45:29 -0800 Subject: [PATCH 210/278] * Add depsgraph handlers to the Qt version of the trace groups editor. This should make the refresh button obsolete, but we'll leave it in there for now * add a function to prefs_utils to let us know if we are in Qt framework mode * fix a couple of issues with iterating over the tree view widget --- rfb_utils/prefs_utils.py | 3 ++ .../rman_operators_editors_tracegroups.py | 33 ++++++++++++++++++- rman_ui/rman_ui_view3d_menus.py | 3 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/rfb_utils/prefs_utils.py b/rfb_utils/prefs_utils.py index dad83def..201fd81c 100644 --- a/rfb_utils/prefs_utils.py +++ b/rfb_utils/prefs_utils.py @@ -12,6 +12,9 @@ def get_addon_prefs(): return v return None +def using_qt(): + return get_pref('rman_ui_framework') == 'QT' + def get_pref(pref_name='', default=None): """ Return the value of a preference diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index fdc2b24c..92f2a0ae 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -101,7 +101,30 @@ def __init__(self) -> None: self.refresh_groups() self.refresh_group_objects() - self.traceGroupObjects.expandAll() + self.traceGroupObjects.expandAll() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(TraceGroupsQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Scene): + self.trace_groups_index_changed() + elif isinstance(dps_update.id, bpy.types.Object) or isinstance(dps_update.id, bpy.types.Collection): + self.refresh_groups() + self.refresh_group_objects() + def update(self): idx = int(self.traceGroups.currentRow()) @@ -169,6 +192,7 @@ def trace_group_changed(self, item): self.label_2.setText("Objects (%s)" % item.text()) def find_item(self, standard_item, ob): + ''' if standard_item.text() == ob.name: return standard_item @@ -178,6 +202,11 @@ def find_item(self, standard_item, ob): return item if item.hasChildren(): return self.find_item(item, ob) + ''' + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item return None @@ -230,6 +259,8 @@ def trace_groups_index_changed(self): current_item = self.traceGroups.currentItem() if current_item: self.label_2.setText("Objects (%s)" % current_item.text()) + else: + return context = bpy.context scene = context.scene rm = scene.renderman diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index 1c277f82..a067e0ef 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -5,6 +5,7 @@ from ..rfb_utils import shadergraph_utils from ..rfb_utils import object_utils from ..rfb_utils.envconfig_utils import envconfig +from ..rfb_utils.prefs_utils import using_qt from ..rfb_logger import rfb_log from ..rman_config import __RFB_CONFIG_DICT__ as rfb_config from bpy.types import Menu @@ -497,7 +498,7 @@ def draw(self, context): for i, obj_grp in enumerate(obj_grps.keys()): op = layout.operator('renderman.add_to_group', text=obj_grp) op.do_scene_selected = True - op.open_editor = True + op.open_editor = not using_qt() op.group_index = i class VIEW3D_MT_RM_Add_Selected_To_LightMixer_Menu(bpy.types.Menu): From 725ecc11b29f0e8980c0315504e1aca823eee32e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Nov 2022 10:52:51 -0800 Subject: [PATCH 211/278] Allow for json config properties to specify a getter function. --- rfb_utils/generate_property_utils.py | 44 ++++++++++--------- .../rfb_node_desc_param.py | 1 + rman_config/__init__.py | 12 ++++- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/rfb_utils/generate_property_utils.py b/rfb_utils/generate_property_utils.py index c95e6999..0713082c 100644 --- a/rfb_utils/generate_property_utils.py +++ b/rfb_utils/generate_property_utils.py @@ -46,7 +46,7 @@ def colorspace_names_list(): pass return items -def generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function): +def generate_string_enum(sp, param_label, param_default, param_help, set_function, get_function, update_function): prop = None if 'ocio_colorspaces' in sp.options: def colorspace_names_options(self, context): @@ -59,6 +59,7 @@ def colorspace_names_options(self, context): description=param_help, items=colorspace_names_options, set=set_function, + get=get_function, update=update_function) else: items = [] @@ -88,6 +89,7 @@ def colorspace_names_options(self, context): default=param_default, description=param_help, items=items, set=set_function, + get=get_function, update=update_function) else: # for strings, assume the first item is the default @@ -97,6 +99,7 @@ def colorspace_names_options(self, context): default=param_default, description=param_help, items=items, set=set_function, + get=get_function, update=update_function) return prop @@ -194,7 +197,7 @@ def is_array(ndp): setattr(node, param_name, sub_prop_names) return True -def generate_property(node, sp, update_function=None, set_function=None): +def generate_property(node, sp, update_function=None, set_function=None, get_function=None): options = {'ANIMATABLE'} param_name = sp._name renderman_name = param_name @@ -312,13 +315,14 @@ def generate_property(node, sp, update_function=None, set_function=None): step=prop_stepsize, description=param_help, set=set_function, + get=get_function, update=update_function) else: if param_widget in ['checkbox', 'switch']: prop = BoolProperty(name=param_label, default=bool(param_default), - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) elif param_widget == 'mapper': items = [] in_items = False @@ -343,7 +347,7 @@ def generate_property(node, sp, update_function=None, set_function=None): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: param_min = sp.min if hasattr(sp, 'min') else (-1.0 * sys.float_info.max) param_max = sp.max if hasattr(sp, 'max') else sys.float_info.max @@ -354,7 +358,7 @@ def generate_property(node, sp, update_function=None, set_function=None): default=param_default, precision=3, soft_min=param_min, soft_max=param_max, step=prop_stepsize, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: param_min = sp.min if hasattr(sp, 'min') else (-1.0 * sys.float_info.max) @@ -366,7 +370,7 @@ def generate_property(node, sp, update_function=None, set_function=None): default=param_default, precision=3, soft_min=param_min, soft_max=param_max, step=prop_stepsize, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = 'float' @@ -375,14 +379,14 @@ def generate_property(node, sp, update_function=None, set_function=None): if sp.is_array(): prop = IntProperty(name=param_label, default=0, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: param_default = int(param_default) if param_default else 0 if param_widget in ['checkbox', 'switch']: prop = BoolProperty(name=param_label, default=bool(param_default), - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) elif param_widget == 'displaymetadata': from ..rman_bl_nodes.rman_bl_nodes_props import RendermanDspyMetaGroup @@ -423,7 +427,7 @@ def generate_property(node, sp, update_function=None, set_function=None): prop = EnumProperty(name=param_label, items=items, default=bl_default, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: param_min = int(sp.min) if hasattr(sp, 'min') else 0 param_max = int(sp.max) if hasattr(sp, 'max') else 2 ** 31 - 1 @@ -432,7 +436,7 @@ def generate_property(node, sp, update_function=None, set_function=None): default=param_default, soft_min=param_min, soft_max=param_max, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: param_min = int(sp.min) if hasattr(sp, 'min') else 0 @@ -442,7 +446,7 @@ def generate_property(node, sp, update_function=None, set_function=None): default=param_default, soft_min=param_min, soft_max=param_max, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = 'int' elif param_type == 'color': @@ -451,7 +455,7 @@ def generate_property(node, sp, update_function=None, set_function=None): default=(1.0, 1.0, 1.0), size=3, subtype="COLOR", soft_min=0.0, soft_max=1.0, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: if param_default == 'null' or param_default is None: param_default = (0.0,0.0,0.0) @@ -459,13 +463,13 @@ def generate_property(node, sp, update_function=None, set_function=None): default=param_default, size=3, subtype="COLOR", soft_min=0.0, soft_max=1.0, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = 'color' elif param_type == 'shader': param_default = '' prop = StringProperty(name=param_label, default=param_default, - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = 'string' elif param_type in ['string', 'struct', 'vstruct', 'bxdf']: if param_default is None: @@ -482,7 +486,7 @@ def generate_property(node, sp, update_function=None, set_function=None): if is_ies: prop = StringProperty(name=param_label, default=param_default, subtype="FILE_PATH", - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) else: prop = StringProperty(name=param_label, default=param_default, subtype="FILE_PATH", @@ -500,7 +504,7 @@ def generate_property(node, sp, update_function=None, set_function=None): description=param_help) elif param_widget in ['mapper', 'popup']: - prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function) + prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, get_function, update_function) elif param_widget == 'bl_scenegraphlocation': reference_type = eval(sp.options['nodeType']) @@ -508,11 +512,11 @@ def generate_property(node, sp, update_function=None, set_function=None): description=param_help, type=reference_type) elif param_widget == 'null' and hasattr(sp, 'options'): - prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, update_function) + prop = generate_string_enum(sp, param_label, param_default, param_help, set_function, get_function, update_function) else: prop = StringProperty(name=param_label, default=str(param_default), - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = param_type elif param_type in ['vector', 'normal']: @@ -521,7 +525,7 @@ def generate_property(node, sp, update_function=None, set_function=None): prop = FloatVectorProperty(name=param_label, default=param_default, size=3, subtype="NONE", - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = param_type elif param_type == 'point': if param_default is None: @@ -529,7 +533,7 @@ def generate_property(node, sp, update_function=None, set_function=None): prop = FloatVectorProperty(name=param_label, default=param_default, size=3, subtype="XYZ", - description=param_help, set=set_function, update=update_function) + description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = param_type elif param_type == 'int2': param_type = 'int' diff --git a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py index 98c1797f..163544a8 100644 --- a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py +++ b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py @@ -12,6 +12,7 @@ NodeDescParamJSON.keywords = NodeDescParamJSON.keywords + ['panel', 'inheritable', 'inherit_true_value', 'update_function_name', 'update_function', 'set_function_name', 'set_function', + 'get_function_name', 'get_function', 'readOnly', 'always_write'] def blender_finalize(obj): diff --git a/rman_config/__init__.py b/rman_config/__init__.py index 2f14189b..3a2fea9a 100644 --- a/rman_config/__init__.py +++ b/rman_config/__init__.py @@ -74,6 +74,7 @@ def addpage(ndp): for param_name, ndp in config.params.items(): update_func = None set_func = None + get_func = None if hasattr(ndp, 'update_function'): # this code tries to dynamically add a function to cls # don't ask me why this works. @@ -94,11 +95,20 @@ def addpage(ndp): elif hasattr(ndp, 'set_function_name'): set_func = ndp.set_function_name + if hasattr(ndp, 'get_function'): + lcls = locals() + exec(ndp.get_function, globals(), lcls) + exec('get_func = %s' % ndp.get_function_name, globals(), lcls) + get_func = lcls['get_func'] + setattr(cls, ndp.get_function_name, get_func) + elif hasattr(ndp, 'get_function_name'): + get_func = ndp.get_function_name + if ndp.is_array(): if generate_property_utils.generate_array_property(cls, prop_names, prop_meta, ndp): addpage(ndp) continue - name, meta, prop = generate_property_utils.generate_property(cls, ndp, update_function=update_func, set_function=set_func) + name, meta, prop = generate_property_utils.generate_property(cls, ndp, update_function=update_func, set_function=set_func, get_function=get_func) if prop: cls.__annotations__[ndp._name] = prop prop_names.append(ndp.name) From b740d7c3320a2cccc192d39b0d8c3bf9c051e031 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 18 Nov 2022 16:36:20 -0800 Subject: [PATCH 212/278] First pass at getting a qt version of lightlinking editor: * add two string properties to the object settings to hold the lighting:excludesubset and lightfilter:subset strings * add a linkingGroup string property to light filters * export the lightfilter:subset attribute on the root node. This should contain all light filters in the scene. RixSceneGraph should take care of flattening the attribute for instances. * For instances, we use "-" to indicate which light filters we want off for the subset. --- rfb_utils/object_utils.py | 8 + rfb_utils/property_callbacks.py | 10 +- rfb_utils/scene_utils.py | 62 +++ rfb_utils/scenegraph_utils.py | 8 +- rman_bl_nodes/rman_bl_nodes_props.py | 6 + .../config/rman_properties_object.json | 24 ++ rman_operators/rman_operators_collections.py | 7 +- .../rman_operators_editors_lightlink.py | 364 ++++++++++++++++++ .../rman_properties_misc/__init__.py | 41 +- .../rman_properties_scene/__init__.py | 4 +- rman_scene.py | 29 +- rman_scene_sync.py | 29 +- rman_translators/rman_light_translator.py | 2 +- .../rman_lightfilter_translator.py | 8 +- rman_translators/rman_translator.py | 33 +- 15 files changed, 541 insertions(+), 94 deletions(-) diff --git a/rfb_utils/object_utils.py b/rfb_utils/object_utils.py index aef5f993..cbcd7120 100644 --- a/rfb_utils/object_utils.py +++ b/rfb_utils/object_utils.py @@ -57,6 +57,14 @@ def get_group_db_name(ob_inst): return string_utils.sanitize_node_name(group_db_name) +def is_light_filter(ob): + if ob is None: + return False + if ob.type != 'LIGHT': + return False + rm = ob.data.renderman + return (rm.renderman_light_role == 'RMAN_LIGHTFILTER') + def is_portal_light(ob): if ob.type != 'LIGHT': return False diff --git a/rfb_utils/property_callbacks.py b/rfb_utils/property_callbacks.py index a980e3c6..4aff3fd8 100644 --- a/rfb_utils/property_callbacks.py +++ b/rfb_utils/property_callbacks.py @@ -216,7 +216,13 @@ def update_root_node_func(self, s, context): scenegraph_utils.update_sg_root_node(s, context) def update_riattr_func(self, s, context): - scenegraph_utils.update_sg_node_riattr(s, context) + ob = None + if not hasattr(context, 'object'): + ob = self.id_data + scenegraph_utils.update_sg_node_riattr(s, context, bl_object=ob) def update_primvar_func(self, s, context): - scenegraph_utils.update_sg_node_primvar(s, context) \ No newline at end of file + ob = None + if not hasattr(context, 'object'): + ob = self.id_data + scenegraph_utils.update_sg_node_primvar(s, context, bl_object=ob) \ No newline at end of file diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index 2851e103..6c7c6504 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -1,6 +1,7 @@ from . import shadergraph_utils from . import object_utils from . import prefs_utils +from . import string_utils from ..rfb_logger import rfb_log import bpy import sys @@ -450,6 +451,67 @@ def find_node_by_name(node_name, ob_name, library=''): return (None, None) +def set_lightlinking_properties(ob, light_ob, illuminate): + light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) + if light_props.renderman_light_role not in {'RMAN_LIGHTFILTER', 'RMAN_LIGHT'}: + return + + light_ob.update_tag(refresh={'DATA'}) + changed = False + if light_props.renderman_light_role == 'RMAN_LIGHT': + exclude_subset = [] + if illuminate == 'OFF': + do_add = True + for j, subset in enumerate(ob.renderman.rman_lighting_excludesubset): + if subset.light_ob == light_ob: + do_add = False + exclude_subset.append('%s' % string_utils.sanitize_node_name(light_ob.name_full)) + if do_add: + subset = ob.renderman.rman_lighting_excludesubset.add() + subset.name = light_ob.name + subset.light_ob = light_ob + changed = True + exclude_subset.append('%s' % string_utils.sanitize_node_name(light_ob.name_full)) + else: + idx = -1 + for j, subset in enumerate(ob.renderman.rman_lighting_excludesubset): + if subset.light_ob == light_ob: + changed = True + idx = j + else: + exclude_subset.append('%s' % string_utils.sanitize_node_name(light_ob.name_full)) + if changed: + ob.renderman.rman_lighting_excludesubset.remove(j) + ob.renderman.rman_lighting_excludesubset_string = ','.join(exclude_subset) + else: + lightfilter_subset = [] + if illuminate == 'OFF': + do_add = True + for j, subset in enumerate(ob.renderman.rman_lightfilter_subset): + if subset.light_ob == light_ob: + do_add = False + lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(light_ob.name_full)) + + if do_add: + subset = ob.renderman.rman_lightfilter_subset.add() + subset.name = light_ob.name + subset.light_ob = light_ob + changed = True + lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(light_ob.name_full)) + else: + idx = -1 + for j, subset in enumerate(ob.renderman.rman_lightfilter_subset): + if subset.light_ob == light_ob: + changed = True + idx = j + else: + lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(light_ob.name_full)) + if changed: + ob.renderman.rman_lightfilter_subset.remove(idx) + ob.renderman.rman_lightfilter_subset_string = ','.join(lightfilter_subset) + + return changed + def is_renderable(scene, ob): return (is_visible_layer(scene, ob) and not ob.hide_render) or \ (ob.type in ['ARMATURE', 'LATTICE', 'EMPTY'] and ob.instance_type not in SUPPORTED_DUPLI_TYPES) diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index 4d95fb8b..706ba12b 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -30,15 +30,15 @@ def update_sg_root_node(prop_name, context): rr = rman_render.RmanRender.get_rman_render() rr.rman_scene_sync.update_root_node_func(prop_name, context) -def update_sg_node_riattr(prop_name, context): +def update_sg_node_riattr(prop_name, context, bl_object=None): from .. import rman_render rr = rman_render.RmanRender.get_rman_render() - rr.rman_scene_sync.update_sg_node_riattr(prop_name, context) + rr.rman_scene_sync.update_sg_node_riattr(prop_name, context, bl_object=bl_object) -def update_sg_node_primvar(prop_name, context): +def update_sg_node_primvar(prop_name, context, bl_object=None): from .. import rman_render rr = rman_render.RmanRender.get_rman_render() - rr.rman_scene_sync.update_sg_node_primvar(prop_name, context) + rr.rman_scene_sync.update_sg_node_primvar(prop_name, context, bl_object=bl_object) def export_vol_aggregate(bl_scene, primvar, ob): vol_aggregate_group = [] diff --git a/rman_bl_nodes/rman_bl_nodes_props.py b/rman_bl_nodes/rman_bl_nodes_props.py index 6b74b61f..b3f188be 100644 --- a/rman_bl_nodes/rman_bl_nodes_props.py +++ b/rman_bl_nodes/rman_bl_nodes_props.py @@ -337,6 +337,12 @@ def update_solo(self, context): description="Lock from changing light shader and light role." ) + linkingGroups: StringProperty( + name="linkingGroups", + default = "", + description="What linking groups this light filter belongs to. These work in conjunction to the 'lightfilter:subset' attrbiute" + ) + # OLD PROPERTIES shadingrate: FloatProperty( diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index 31c6d5c1..13fe11c6 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -568,6 +568,30 @@ "options": "prop_parent:context.scene.renderman|prop_name:object_groups", "help": "Object groups visible to transmission/refractions" }, + { + "panel": "", + "name": "rman_lighting_excludesubset_string", + "riattr": "lighting:excludesubset", + "label": "Lighting Exclude Subset", + "type": "string", + "default": "", + "page": "", + "editable": true, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_lighting_excludesubset_string', c)", + "help": "Lighting exclude subset" + }, + { + "panel": "", + "name": "rman_lightfilter_subset_string", + "riattr": "lightfilter:subset", + "label": "Light Filter Subset", + "type": "string", + "default": "", + "page": "", + "editable": true, + "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_lightfilter_subset_string', c)", + "help": "Light filter subset" + }, { "panel": "OBJECT_PT_renderman_object_raytracing", "name": "rman_intersectPriority", diff --git a/rman_operators/rman_operators_collections.py b/rman_operators/rman_operators_collections.py index 15dd2c2e..fbcd7f02 100644 --- a/rman_operators/rman_operators_collections.py +++ b/rman_operators/rman_operators_collections.py @@ -3,6 +3,7 @@ from ..rfb_logger import rfb_log from ..rfb_utils import shadergraph_utils from ..rfb_utils import scenegraph_utils +from ..rfb_utils import object_utils from ..rfb_utils.rman_socket_utils import node_add_input import bpy @@ -634,7 +635,8 @@ def add_selected(self, context): if op: op.selected_light_name = '0' - light_ob.update_tag(refresh={'DATA'}) + if not object_utils.is_light_filter(light_ob): + light_ob.update_tag(refresh={'DATA'}) def add_scene_selected(self, context): scene = context.scene @@ -669,7 +671,8 @@ def add_scene_selected(self, context): ll = scene.renderman.light_links.add() ll.name = light_ob.name ll.light_ob = light_ob.data - light_ob.update_tag(refresh={'DATA'}) + if not object_utils.is_light_filter(light_ob): + light_ob.update_tag(refresh={'DATA'}) def execute(self, context): if self.properties.do_scene_selected: diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 19603b10..e79aa2a7 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -2,12 +2,357 @@ from ...rfb_utils import scene_utils from ...rfb_utils import shadergraph_utils +from ...rfb_utils import string_utils +from ...rfb_utils import scenegraph_utils from ...rfb_logger import rfb_log +from ...rfb_utils.prefs_utils import get_pref +from ...rfb_utils import object_utils from ... import rfb_icons from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config import bpy import re +import sys + +rfb_qt = None +__LIGHT_LINKING_WINDOW__ = None +try: + from ...rman_ui import rfb_qt as rfb_qt +except: + pass + +if rfb_qt: + + from PySide2 import QtCore, QtWidgets, QtGui + class LightLinkingQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.light_linking_qt_app_timed" + bl_label = "Light Linking Editor" + + def __init__(self): + super(LightLinkingQtAppTimed, self).__init__() + + def execute(self, context): + self._window = LightLinkingQtWrapper() + return super(LightLinkingQtAppTimed, self).execute(context) + + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + + class LightLinkingQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(LightLinkingQtWrapper, self).__init__() + self.setObjectName("Dialog") + self.resize(825, 526) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + #self.invert_checkBox = QtWidgets.QCheckBox(self) + #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) + #self.invert_checkBox.setObjectName("invert_checkBox") + self.widget = QtWidgets.QWidget(self) + self.widget.setGeometry(QtCore.QRect(40, 70, 751, 361)) + self.widget.setObjectName("widget") + self.gridLayout = QtWidgets.QGridLayout(self.widget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setHorizontalSpacing(50) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.lights_label = QtWidgets.QLabel(self.widget) + self.lights_label.setObjectName("lights_label") + self.verticalLayout.addWidget(self.lights_label) + self.lights_treeView = QtWidgets.QListWidget(self) + self.lights_treeView.setObjectName("lights_treeView") + self.verticalLayout.addWidget(self.lights_treeView) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.objects_label = QtWidgets.QLabel(self.widget) + self.objects_label.setObjectName("objects_label") + self.verticalLayout_2.addWidget(self.objects_label) + + self.objects_treeView = QtWidgets.QTreeView(self.widget) + self.objects_treeView.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + self.objects_treeView.setObjectName("objects_treeView") + self.objects_treeView.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.objects_treeView.setModel(self.treeModel) + + self.verticalLayout_2.addWidget(self.objects_treeView) + self.gridLayout.addLayout(self.verticalLayout_2, 0, 1, 1, 1) + + self.lights_treeView.itemSelectionChanged.connect(self.lights_index_changed) + + self.objects_treeView.selectionModel().selectionChanged.connect(self.linked_objects_selection) + + self.light_link_item = None + self.total_objects = 0 + self.retranslateUi() + self.refresh_lights() + + self.add_handlers() + + + def retranslateUi(self): + _translate = QtCore.QCoreApplication.translate + self.setWindowTitle(_translate("Dialog", "Light Linking")) + #self.invert_checkBox.setToolTip(_translate("Dialog", "Invert light linking")) + #self.invert_checkBox.setText(_translate("Dialog", "Invert")) + self.lights_label.setText(_translate("Dialog", "Lights")) + self.objects_label.setText(_translate("Dialog", "Objects")) + + def closeEvent(self, event): + self.remove_handlers() + super(LightLinkingQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.refresh_lights() + self.refresh_linked_objects() + self.lights_index_changed() + elif isinstance(dps_update.id, bpy.types.Scene): + self.refresh_lights() + + def update(self): + super(LightLinkingQtWrapper, self).update() + + + def refresh_lights(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + all_lights = [l.name for l in scene_utils.get_all_lights(scene, include_light_filters=True)] + remove_items = [] + + for i in range(self.lights_treeView.count()): + item = self.lights_treeView.item(i) + name = item.text() + if name not in all_lights: + remove_items.append(item) + else: + all_lights.remove(name) + + for nm in all_lights: + item = QtWidgets.QListWidgetItem(nm) + item.setFlags(item.flags()) + self.lights_treeView.addItem(item) + + for item in remove_items: + self.lights_treeView.takeItem(self.lights_treeView.row(item)) + del item + + if remove_items or len(all_lights) > 0: + self.lights_treeView.setCurrentRow(-1) + + def find_light_link_item(self, light_nm=''): + context = bpy.context + scene = context.scene + rm = scene.renderman + light_link_item = None + for ll in rm.light_links: + if ll.light_ob.name == light_nm: + light_link_item = ll + break + return light_link_item + + def lights_index_changed(self): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + self.treeModel.clear() + self.objects_treeView.selectionModel().select(QtCore.QItemSelection(), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + return + self.rootNode = self.treeModel.invisibleRootItem() + if self.rootNode.rowCount() == 0: + self.refresh_linked_objects() + context = bpy.context + scene = context.scene + rm = scene.renderman + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + + light_link_item = self.find_light_link_item(light_nm) + selected_items = QtCore.QItemSelection() + + if light_link_item is None: + if not object_utils.is_light_filter(light_ob): + light_link_item = scene.renderman.light_links.add() + light_link_item.name = light_ob.name + light_link_item.light_ob = light_ob + + for i in range(0, self.rootNode.rowCount()): + item = self.rootNode.child(i) + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + else: + for i in range(0, self.rootNode.rowCount()): + item = self.rootNode.child(i) + if not item: + continue + idx = self.treeModel.indexFromItem(item) + ob_nm = item.text() + found = False + for member in light_link_item.members: + ob = member.ob_pointer + if ob is None: + continue + if ob.name == ob_nm: + found = True + break + + if found: + continue + + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + self.objects_treeView.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if not item: + continue + if item.text() == ob.name: + return item + if item.rowCount() > 0: + return self.find_item(item, ob) + + return None + + def get_all_removed_items(self, standard_item, scene, remove_items): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if not item: + continue + nm = item.text() + if nm not in scene.objects: + remove_items.append(item) + if item.rowCount() > 0: + self.get_all_removed_items(item, scene, remove_items) + + + def refresh_linked_objects(self): + context = bpy.context + scene = context.scene + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: + if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + continue + item = self.find_item(root_item, child) + if not item: + item = StandardItem(txt=child.name) + self.total_objects += 1 + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + remove_items = [] + self.get_all_removed_items(self.rootNode, scene, remove_items) + + for item in remove_items: + self.treeModel.takeRow(item.row()) + self.total_objects -= 1 + del item + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: + if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + continue + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.total_objects += 1 + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.objects_treeView.expandAll() + + def linked_objects_selection(self, selected, deselected): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) + is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' + ll = self.find_light_link_item(light_nm) + + if ll is None: + ll = scene.renderman.light_links.add() + ll.name = light_ob.name + ll.light_ob = light_ob + + if is_light_filter: + # linkingGroups should only be set if one of the items is deselected + total_selected_items = len(self.objects_treeView.selectionModel().selectedIndexes()) + + if total_selected_items == self.total_objects and light_props.linkingGroups != "": + light_props.linkingGroups = "" + light_ob.update_tag(refresh={'DATA'}) + elif total_selected_items != self.total_objects and light_props.linkingGroups == "": + light_props.linkingGroups = string_utils.sanitize_node_name(light_ob.name_full) + light_ob.update_tag(refresh={'DATA'}) + + for i in deselected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in ll.members: + if ob == member.ob_pointer: + do_add = False + break + + if do_add: + member = ll.members.add() + member.name = ob.name + member.ob_pointer = ob + member.illuminate = 'OFF' + + scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) + + for i in selected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_remove = False + idx = -1 + for i, member in enumerate(ll.members): + if ob == member.ob_pointer: + do_remove = True + idx = i + break + if do_remove: + member = ll.members.remove(idx) + + scene_utils.set_lightlinking_properties(ob, light_ob, '') class RENDERMAN_UL_LightLink_Light_List(bpy.types.UIList): @@ -276,6 +621,13 @@ def check_light_links(self, context): if lg.light_ob is None or lg.light_ob.name not in scene.objects: delete_any.insert(0, i) continue + + if object_utils.is_light_filter(lg.light_ob): + if lg.light_ob.data.renderman.linkingGroups == "": + lg.light_ob.data.renderman.linkingGroups = string_utils.sanitize_node_name(lg.light_ob.name_full) + else: + lg.light_ob.data.renderman.linkingGroups = "" + delete_objs = [] for j in range(len(lg.members)-1, -1, -1): member = lg.members[j] @@ -290,6 +642,18 @@ def check_light_links(self, context): rm.light_links_index -= 1 def invoke(self, context, event): + + ''' + if rfb_qt and get_pref('rman_ui_framework') == 'QT': + global __LIGHT_LINKING_WINDOW__ + if sys.platform == "darwin": + rfb_qt.run_with_timer(__LIGHT_LINKING_WINDOW__, LightLinkingQtWrapper) + else: + bpy.ops.wm.light_linking_qt_app_timed() + + return {'RUNNING_MODAL'} + ''' + wm = context.window_manager width = rfb_config['editor_preferences']['lightlink_editor']['width'] self.event = event diff --git a/rman_properties/rman_properties_misc/__init__.py b/rman_properties/rman_properties_misc/__init__.py index dc595d4e..56492215 100644 --- a/rman_properties/rman_properties_misc/__init__.py +++ b/rman_properties/rman_properties_misc/__init__.py @@ -3,7 +3,9 @@ CollectionProperty, BoolVectorProperty, IntVectorProperty from ...rfb_utils import shadergraph_utils +from ...rfb_utils import scene_utils from ...rfb_logger import rfb_log +from ...rfb_utils.prefs_utils import using_qt from ... import rman_config import bpy @@ -61,47 +63,24 @@ def update_name(self, context): name: StringProperty(name="name", update=update_name) def update_ob_pointer(self, context): - self.ob_pointer.update_tag(refresh={'OBJECT'}) + if not using_qt(): + self.ob_pointer.update_tag(refresh={'OBJECT'}) ob_pointer: PointerProperty(type=bpy.types.Object, update=update_ob_pointer) def update_link(self, context): light_ob = getattr(context, 'light_ob', None) - if not light_ob: + if not light_ob and hasattr(context, 'active_object'): light_ob = context.active_object - if light_ob.type != 'LIGHT': + if light_ob and light_ob.type != 'LIGHT': return - - light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) - if light_props.renderman_light_role not in {'RMAN_LIGHTFILTER', 'RMAN_LIGHT'}: + if not light_ob: return - light_ob.update_tag(refresh={'DATA'}) - ob = self.ob_pointer - light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) - if light_props.renderman_light_role == 'RMAN_LIGHT': - if self.illuminate == 'OFF': - subset = ob.renderman.rman_lighting_excludesubset.add() - subset.name = light_ob.name - subset.light_ob = light_ob - else: - for j, subset in enumerate(ob.renderman.rman_lighting_excludesubset): - if subset.light_ob == light_ob: - ob.renderman.rman_lighting_excludesubset.remove(j) - break - else: - if self.illuminate == 'OFF': - for j, subset in enumerate(ob.renderman.rman_lightfilter_subset): - if subset.light_ob == light_ob: - ob.renderman.rman_lightfilter_subset.remove(j) - break - else: - subset = ob.renderman.rman_lightfilter_subset.add() - subset.name = light_ob.name - subset.light_ob = light_ob - - ob.update_tag(refresh={'OBJECT'}) + if scene_utils.set_lightlinking_properties(ob, light_ob, self.illuminate): + if not using_qt(): + ob.update_tag(refresh={'DATA'}) illuminate: EnumProperty( name="Illuminate", diff --git a/rman_properties/rman_properties_scene/__init__.py b/rman_properties/rman_properties_scene/__init__.py index 832da0ac..11b49c84 100644 --- a/rman_properties/rman_properties_scene/__init__.py +++ b/rman_properties/rman_properties_scene/__init__.py @@ -3,7 +3,7 @@ CollectionProperty from ...rfb_utils.envconfig_utils import envconfig -from ...rfb_utils.prefs_utils import get_pref +from ...rfb_utils.prefs_utils import get_pref, using_qt from ...rfb_logger import rfb_log from ... import rman_bl_nodes from ...rman_bl_nodes import rman_bl_nodes_props @@ -132,7 +132,7 @@ def get_is_rman_viewport_rendering(self): return (rman_render.rman_is_viewport_rendering or is_shading) def get_light_linking_inverted(self): - return get_pref('rman_invert_light_linking') + return get_pref('rman_invert_light_linking') and not using_qt() current_platform: StringProperty(get=get_platform) is_ncr_license: BoolProperty(get=get_is_ncr_license) diff --git a/rman_scene.py b/rman_scene.py index a339bda6..07ed4ff9 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -504,19 +504,16 @@ def export_swatch_render_scene(self): self.export_data_blocks() - def export_root_sg_node(self): - + def set_root_lightlinks(self, rixattrs=None): rm = self.bl_scene.renderman root_sg = self.get_root_sg_node() - attrs = root_sg.GetAttributes() - - # set any properties marked riattr in the config file - for prop_name, meta in rm.prop_meta.items(): - property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False, remove=False) + attrs = rixattrs + if rixattrs is None: + attrs = root_sg.GetAttributes() + all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(self.bl_scene)] if rm.invert_light_linking: all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(self.bl_scene, include_light_filters=False)] - all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(self.bl_scene)] for ll in rm.light_links: light_ob = ll.light_ob light_nm = string_utils.sanitize_node_name(light_ob.name) @@ -536,7 +533,23 @@ def export_root_sg_node(self): attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, ' '. join(all_lightfilters) ) else: attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, '*') + else: + attrs.SetString(self.rman.Tokens.Rix.k_lightfilter_subset, ','. join(all_lightfilters) ) + + if rixattrs is None: + root_sg.SetAttributes(attrs) + + def export_root_sg_node(self): + + rm = self.bl_scene.renderman + root_sg = self.get_root_sg_node() + attrs = root_sg.GetAttributes() + + # set any properties marked riattr in the config file + for prop_name, meta in rm.prop_meta.items(): + property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False, remove=False) + self.set_root_lightlinks(rixattrs=attrs) root_sg.SetAttributes(attrs) def get_root_sg_node(self): diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 03737ecd..ec6db33e 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -443,12 +443,12 @@ def check_object_datablock(self, dps_update): self.camera_updated(dps_update) elif rman_type == 'EMPTY_INSTANCER': self.check_empty_instancer(dps_update) - else: - rfb_log().debug("\tObject: %s Updated" % dps_update.id.name) - rfb_log().debug("\t is_updated_geometry: %s" % str(dps_update.is_updated_geometry)) - rfb_log().debug("\t is_updated_shading: %s" % str(dps_update.is_updated_shading)) - rfb_log().debug("\t is_updated_transform: %s" % str(dps_update.is_updated_transform)) - if dps_update.id.original not in self.rman_updates: + else: + if dps_update.id.original not in self.rman_updates: + rfb_log().debug("\tObject: %s Updated" % dps_update.id.name) + rfb_log().debug("\t is_updated_geometry: %s" % str(dps_update.is_updated_geometry)) + rfb_log().debug("\t is_updated_shading: %s" % str(dps_update.is_updated_shading)) + rfb_log().debug("\t is_updated_transform: %s" % str(dps_update.is_updated_transform)) rman_update = RmanUpdate() rman_update.is_updated_geometry = dps_update.is_updated_geometry rman_update.is_updated_shading = dps_update.is_updated_shading @@ -761,6 +761,7 @@ def check_instances(self, batch_mode=False): for o in users[ob_eval.original]: if isinstance(o, bpy.types.Light): o.node_tree.update_tag() + self.rman_scene.set_root_lightlinks() # update lightlinking on the root node continue is_new_object = True @@ -992,23 +993,29 @@ def update_root_node_func(self, prop_name, context): property_utils.set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=False) root_sg.SetAttributes(attrs) - def update_sg_node_riattr(self, prop_name, context): + def update_sg_node_riattr(self, prop_name, context, bl_object=None): if not self.rman_render.rman_interactive_running: return self.rman_scene.bl_scene = context.scene - ob = context.object + if bl_object: + ob = bl_object + else: + ob = context.object rman_update = RmanUpdate() rman_update.do_clear_instances = False rman_update.is_updated_attributes = True rman_update.updated_prop_name = prop_name self.rman_updates[ob.original] = rman_update - rfb_log().debug("Updated RiAttribute: %s" % rman_update.updated_prop_name) + rfb_log().debug("Updated RiAttribute: %s (%s)" % (rman_update.updated_prop_name, ob.name)) - def update_sg_node_primvar(self, prop_name, context): + def update_sg_node_primvar(self, prop_name, context, bl_object=None): if not self.rman_render.rman_interactive_running: return self.rman_scene.bl_scene = context.scene - ob = context.object + if bl_object: + ob = bl_object + else: + ob = context.object rman_update = RmanUpdate() rman_update.do_clear_instances = False rman_update.is_updated_geometry = True diff --git a/rman_translators/rman_light_translator.py b/rman_translators/rman_light_translator.py index c6ee2197..1fa5f856 100644 --- a/rman_translators/rman_light_translator.py +++ b/rman_translators/rman_light_translator.py @@ -68,7 +68,7 @@ def update_light_attributes(self, ob, rman_sg_light): attrs.SetInteger("visibility:camera", int(primary_vis)) attrs.SetInteger("visibility:transmission", 0) attrs.SetInteger("visibility:indirect", 0) - obj_groups_str = "World,%s" % rman_sg_light.db_name + obj_groups_str = "World,%s" % ob.name_full attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_grouping_membership, obj_groups_str) rman_sg_light.sg_node.SetAttributes(attrs) diff --git a/rman_translators/rman_lightfilter_translator.py b/rman_translators/rman_lightfilter_translator.py index 0e808d2e..0db705a2 100644 --- a/rman_translators/rman_lightfilter_translator.py +++ b/rman_translators/rman_lightfilter_translator.py @@ -104,7 +104,7 @@ def update(self, ob, rman_sg_lightfilter): rixparams.SetString("coordsys", rman_sg_lightfilter.coord_sys) # check if this light filter belongs to a light link - for ll in self.rman_scene.bl_scene.renderman.light_links: - if ll.light_ob == ob: - rixparams.SetString("linkingGroups", ob.name) - break \ No newline at end of file + if ob.original.data.renderman.linkingGroups != "": + rixparams.SetString("linkingGroups", ob.original.data.renderman.linkingGroups) + else: + rixparams.Remove("linkingGroups") \ No newline at end of file diff --git a/rman_translators/rman_translator.py b/rman_translators/rman_translator.py index 593cdadc..a940d465 100644 --- a/rman_translators/rman_translator.py +++ b/rman_translators/rman_translator.py @@ -143,13 +143,13 @@ def export_instance_attributes(self, ob, rman_sg_node, ob_inst): def export_light_linking_attributes(self, ob, attrs): rm = ob.renderman + bl_scene = self.rman_scene.bl_scene + all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(bl_scene)] if self.rman_scene.bl_scene.renderman.invert_light_linking: lighting_subset = [] lightfilter_subset = [] - bl_scene = self.rman_scene.bl_scene all_lights = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lights(bl_scene, include_light_filters=False)] - all_lightfilters = [string_utils.sanitize_node_name(l.name) for l in scene_utils.get_all_lightfilters(bl_scene)] for ll in self.rman_scene.bl_scene.renderman.light_links: light_ob = ll.light_ob light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) @@ -173,37 +173,12 @@ def export_light_linking_attributes(self, ob, attrs): if lighting_subset: lighting_subset = lighting_subset + all_lights # include all other lights that are not linked - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lighting_subset, ' '. join(lighting_subset) ) + attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lighting_subset, ','. join(lighting_subset) ) if lightfilter_subset: lightfilter_subset = lightfilter_subset + all_lightfilters - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lightfilter_subset, ' ' . join(lightfilter_subset)) + attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lightfilter_subset, ',' . join(lightfilter_subset)) - else: - exclude_subset = [] - lightfilter_subset = [] - for subset in rm.rman_lighting_excludesubset: - if subset.light_ob is None: - continue - nm = string_utils.sanitize_node_name(subset.light_ob.name) - exclude_subset.append(nm) - - for subset in rm.rman_lightfilter_subset: - if subset.light_ob is None: - continue - nm = string_utils.sanitize_node_name(subset.light_ob.name) - lightfilter_subset.append(nm) - - if exclude_subset: - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lighting_excludesubset, ' '. join(exclude_subset) ) - else: - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lighting_excludesubset, '') - - if lightfilter_subset: - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lightfilter_subset, ' ' . join(lightfilter_subset)) - else: - attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_lightfilter_subset, '') - def export_object_attributes(self, ob, rman_sg_node, remove=True): if not rman_sg_node.sg_node: return From 0b8122dff9624d9c8728a080763e177c18dcea87 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 22 Nov 2022 05:23:39 -0800 Subject: [PATCH 213/278] Merge the Qt specific code with the Blender native operator code, so that we no longer need to separately register the Qt apps. Also, for now, hide the light linking and trace sets Qt apps behind the RFB_DEVELOPER env var. --- .../rman_operators_editors_lightlink.py | 666 +++++++++--------- .../rman_operators_editors_tracegroups.py | 594 ++++++++-------- rman_presets/qt_app.py | 85 --- rman_presets/ui.py | 76 +- rman_stats/__init__.py | 16 +- rman_stats/operators.py | 6 +- rman_ui/rman_ui_txmanager.py | 124 +++- rman_ui/rman_ui_txmanager_qt.py | 104 --- rman_ui/rman_ui_view3d_menus.py | 2 +- rman_ui/rman_ui_view3d_panels.py | 2 +- 10 files changed, 798 insertions(+), 877 deletions(-) delete mode 100644 rman_presets/qt_app.py delete mode 100644 rman_ui/rman_ui_txmanager_qt.py diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index e79aa2a7..b6726900 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -5,354 +5,351 @@ from ...rfb_utils import string_utils from ...rfb_utils import scenegraph_utils from ...rfb_logger import rfb_log -from ...rfb_utils.prefs_utils import get_pref +from ...rfb_utils.prefs_utils import get_pref, using_qt from ...rfb_utils import object_utils +from ...rfb_utils.envconfig_utils import envconfig from ... import rfb_icons from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config +from ...rman_ui import rfb_qt as rfb_qt import bpy import re import sys +from PySide2 import QtCore, QtWidgets, QtGui + -rfb_qt = None __LIGHT_LINKING_WINDOW__ = None -try: - from ...rman_ui import rfb_qt as rfb_qt -except: - pass - -if rfb_qt: - - from PySide2 import QtCore, QtWidgets, QtGui - class LightLinkingQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.light_linking_qt_app_timed" - bl_label = "Light Linking Editor" - - def __init__(self): - super(LightLinkingQtAppTimed, self).__init__() - - def execute(self, context): - self._window = LightLinkingQtWrapper() - return super(LightLinkingQtAppTimed, self).execute(context) - - class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - - class LightLinkingQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(LightLinkingQtWrapper, self).__init__() - self.setObjectName("Dialog") - self.resize(825, 526) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - #self.invert_checkBox = QtWidgets.QCheckBox(self) - #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) - #self.invert_checkBox.setObjectName("invert_checkBox") - self.widget = QtWidgets.QWidget(self) - self.widget.setGeometry(QtCore.QRect(40, 70, 751, 361)) - self.widget.setObjectName("widget") - self.gridLayout = QtWidgets.QGridLayout(self.widget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setHorizontalSpacing(50) - self.gridLayout.setObjectName("gridLayout") - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.lights_label = QtWidgets.QLabel(self.widget) - self.lights_label.setObjectName("lights_label") - self.verticalLayout.addWidget(self.lights_label) - self.lights_treeView = QtWidgets.QListWidget(self) - self.lights_treeView.setObjectName("lights_treeView") - self.verticalLayout.addWidget(self.lights_treeView) - self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.objects_label = QtWidgets.QLabel(self.widget) - self.objects_label.setObjectName("objects_label") - self.verticalLayout_2.addWidget(self.objects_label) - - self.objects_treeView = QtWidgets.QTreeView(self.widget) - self.objects_treeView.setSelectionMode( - QtWidgets.QAbstractItemView.MultiSelection - ) - self.objects_treeView.setObjectName("objects_treeView") - self.objects_treeView.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.objects_treeView.setModel(self.treeModel) - - self.verticalLayout_2.addWidget(self.objects_treeView) - self.gridLayout.addLayout(self.verticalLayout_2, 0, 1, 1, 1) - - self.lights_treeView.itemSelectionChanged.connect(self.lights_index_changed) - - self.objects_treeView.selectionModel().selectionChanged.connect(self.linked_objects_selection) - - self.light_link_item = None - self.total_objects = 0 - self.retranslateUi() - self.refresh_lights() - - self.add_handlers() - - - def retranslateUi(self): - _translate = QtCore.QCoreApplication.translate - self.setWindowTitle(_translate("Dialog", "Light Linking")) - #self.invert_checkBox.setToolTip(_translate("Dialog", "Invert light linking")) - #self.invert_checkBox.setText(_translate("Dialog", "Invert")) - self.lights_label.setText(_translate("Dialog", "Lights")) - self.objects_label.setText(_translate("Dialog", "Objects")) - - def closeEvent(self, event): - self.remove_handlers() - super(LightLinkingQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Collection): - self.refresh_lights() - self.refresh_linked_objects() - self.lights_index_changed() - elif isinstance(dps_update.id, bpy.types.Scene): - self.refresh_lights() - - def update(self): - super(LightLinkingQtWrapper, self).update() - - - def refresh_lights(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - all_lights = [l.name for l in scene_utils.get_all_lights(scene, include_light_filters=True)] - remove_items = [] - - for i in range(self.lights_treeView.count()): - item = self.lights_treeView.item(i) - name = item.text() - if name not in all_lights: - remove_items.append(item) - else: - all_lights.remove(name) - - for nm in all_lights: - item = QtWidgets.QListWidgetItem(nm) - item.setFlags(item.flags()) - self.lights_treeView.addItem(item) - - for item in remove_items: - self.lights_treeView.takeItem(self.lights_treeView.row(item)) - del item - - if remove_items or len(all_lights) > 0: - self.lights_treeView.setCurrentRow(-1) - - def find_light_link_item(self, light_nm=''): - context = bpy.context - scene = context.scene - rm = scene.renderman - light_link_item = None - for ll in rm.light_links: - if ll.light_ob.name == light_nm: - light_link_item = ll - break - return light_link_item - - def lights_index_changed(self): - idx = int(self.lights_treeView.currentRow()) - current_item = self.lights_treeView.currentItem() - if not current_item: - self.treeModel.clear() - self.objects_treeView.selectionModel().select(QtCore.QItemSelection(), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - return - self.rootNode = self.treeModel.invisibleRootItem() - if self.rootNode.rowCount() == 0: + + +class LightLinkingQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.light_linking_qt_app_timed" + bl_label = "Light Linking Editor" + + def __init__(self): + super(LightLinkingQtAppTimed, self).__init__() + + def execute(self, context): + self._window = LightLinkingQtWrapper() + return super(LightLinkingQtAppTimed, self).execute(context) + +class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + +class LightLinkingQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(LightLinkingQtWrapper, self).__init__() + self.setObjectName("Dialog") + self.resize(825, 526) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + #self.invert_checkBox = QtWidgets.QCheckBox(self) + #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) + #self.invert_checkBox.setObjectName("invert_checkBox") + self.widget = QtWidgets.QWidget(self) + self.widget.setGeometry(QtCore.QRect(40, 70, 751, 361)) + self.widget.setObjectName("widget") + self.gridLayout = QtWidgets.QGridLayout(self.widget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setHorizontalSpacing(50) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.lights_label = QtWidgets.QLabel(self.widget) + self.lights_label.setObjectName("lights_label") + self.verticalLayout.addWidget(self.lights_label) + self.lights_treeView = QtWidgets.QListWidget(self) + self.lights_treeView.setObjectName("lights_treeView") + self.verticalLayout.addWidget(self.lights_treeView) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.objects_label = QtWidgets.QLabel(self.widget) + self.objects_label.setObjectName("objects_label") + self.verticalLayout_2.addWidget(self.objects_label) + + self.objects_treeView = QtWidgets.QTreeView(self.widget) + self.objects_treeView.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + self.objects_treeView.setObjectName("objects_treeView") + self.objects_treeView.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.objects_treeView.setModel(self.treeModel) + + self.verticalLayout_2.addWidget(self.objects_treeView) + self.gridLayout.addLayout(self.verticalLayout_2, 0, 1, 1, 1) + + self.lights_treeView.itemSelectionChanged.connect(self.lights_index_changed) + + self.objects_treeView.selectionModel().selectionChanged.connect(self.linked_objects_selection) + + self.light_link_item = None + self.total_objects = 0 + self.retranslateUi() + self.refresh_lights() + + self.add_handlers() + + + def retranslateUi(self): + _translate = QtCore.QCoreApplication.translate + self.setWindowTitle(_translate("Dialog", "Light Linking")) + #self.invert_checkBox.setToolTip(_translate("Dialog", "Invert light linking")) + #self.invert_checkBox.setText(_translate("Dialog", "Invert")) + self.lights_label.setText(_translate("Dialog", "Lights")) + self.objects_label.setText(_translate("Dialog", "Objects")) + + def closeEvent(self, event): + self.remove_handlers() + super(LightLinkingQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.refresh_lights() self.refresh_linked_objects() - context = bpy.context - scene = context.scene - rm = scene.renderman - light_nm = current_item.text() - light_ob = context.scene.objects.get(light_nm, None) - - light_link_item = self.find_light_link_item(light_nm) - selected_items = QtCore.QItemSelection() - - if light_link_item is None: - if not object_utils.is_light_filter(light_ob): - light_link_item = scene.renderman.light_links.add() - light_link_item.name = light_ob.name - light_link_item.light_ob = light_ob - - for i in range(0, self.rootNode.rowCount()): - item = self.rootNode.child(i) - idx = self.treeModel.indexFromItem(item) - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - else: - for i in range(0, self.rootNode.rowCount()): - item = self.rootNode.child(i) - if not item: - continue - idx = self.treeModel.indexFromItem(item) - ob_nm = item.text() - found = False - for member in light_link_item.members: - ob = member.ob_pointer - if ob is None: - continue - if ob.name == ob_nm: - found = True - break - - if found: - continue - - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - self.objects_treeView.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - - def find_item(self, standard_item, ob): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) + self.lights_index_changed() + elif isinstance(dps_update.id, bpy.types.Scene): + self.refresh_lights() + + def update(self): + super(LightLinkingQtWrapper, self).update() + + + def refresh_lights(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + all_lights = [l.name for l in scene_utils.get_all_lights(scene, include_light_filters=True)] + remove_items = [] + + for i in range(self.lights_treeView.count()): + item = self.lights_treeView.item(i) + name = item.text() + if name not in all_lights: + remove_items.append(item) + else: + all_lights.remove(name) + + for nm in all_lights: + item = QtWidgets.QListWidgetItem(nm) + item.setFlags(item.flags()) + self.lights_treeView.addItem(item) + + for item in remove_items: + self.lights_treeView.takeItem(self.lights_treeView.row(item)) + del item + + if remove_items or len(all_lights) > 0: + self.lights_treeView.setCurrentRow(-1) + + def find_light_link_item(self, light_nm=''): + context = bpy.context + scene = context.scene + rm = scene.renderman + light_link_item = None + for ll in rm.light_links: + if ll.light_ob.name == light_nm: + light_link_item = ll + break + return light_link_item + + def lights_index_changed(self): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + self.treeModel.clear() + self.objects_treeView.selectionModel().select(QtCore.QItemSelection(), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + return + self.rootNode = self.treeModel.invisibleRootItem() + if self.rootNode.rowCount() == 0: + self.refresh_linked_objects() + context = bpy.context + scene = context.scene + rm = scene.renderman + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + + light_link_item = self.find_light_link_item(light_nm) + selected_items = QtCore.QItemSelection() + + if light_link_item is None: + if not object_utils.is_light_filter(light_ob): + light_link_item = scene.renderman.light_links.add() + light_link_item.name = light_ob.name + light_link_item.light_ob = light_ob + + for i in range(0, self.rootNode.rowCount()): + item = self.rootNode.child(i) + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + else: + for i in range(0, self.rootNode.rowCount()): + item = self.rootNode.child(i) if not item: continue - if item.text() == ob.name: - return item - if item.rowCount() > 0: - return self.find_item(item, ob) - - return None + idx = self.treeModel.indexFromItem(item) + ob_nm = item.text() + found = False + for member in light_link_item.members: + ob = member.ob_pointer + if ob is None: + continue + if ob.name == ob_nm: + found = True + break - def get_all_removed_items(self, standard_item, scene, remove_items): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if not item: + if found: continue - nm = item.text() - if nm not in scene.objects: - remove_items.append(item) - if item.rowCount() > 0: - self.get_all_removed_items(item, scene, remove_items) - - def refresh_linked_objects(self): - context = bpy.context - scene = context.scene - self.rootNode = self.treeModel.invisibleRootItem() - - def add_children(root_item, ob): - for child in ob.children: - if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: - continue - item = self.find_item(root_item, child) - if not item: - item = StandardItem(txt=child.name) - self.total_objects += 1 - root_item.appendRow(item) - if len(child.children) > 0: - add_children(item, child) - - remove_items = [] - self.get_all_removed_items(self.rootNode, scene, remove_items) - - for item in remove_items: - self.treeModel.takeRow(item.row()) - self.total_objects -= 1 - del item - - root_parents = [ob for ob in scene.objects if ob.parent is None] - for ob in root_parents: + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + self.objects_treeView.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if not item: + continue + if item.text() == ob.name: + return item + if item.rowCount() > 0: + return self.find_item(item, ob) + + return None + + def get_all_removed_items(self, standard_item, scene, remove_items): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if not item: + continue + nm = item.text() + if nm not in scene.objects: + remove_items.append(item) + if item.rowCount() > 0: + self.get_all_removed_items(item, scene, remove_items) + + + def refresh_linked_objects(self): + context = bpy.context + scene = context.scene + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: - continue - item = self.find_item(self.rootNode, ob) + continue + item = self.find_item(root_item, child) if not item: - item = StandardItem(txt=ob.name) - self.total_objects += 1 - self.rootNode.appendRow(item) - if len(ob.children) > 0: - add_children(item, ob) - - self.objects_treeView.expandAll() - - def linked_objects_selection(self, selected, deselected): - idx = int(self.lights_treeView.currentRow()) - current_item = self.lights_treeView.currentItem() - if not current_item: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - light_nm = current_item.text() - light_ob = context.scene.objects.get(light_nm, None) - light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) - is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' - ll = self.find_light_link_item(light_nm) - - if ll is None: - ll = scene.renderman.light_links.add() - ll.name = light_ob.name - ll.light_ob = light_ob - - if is_light_filter: - # linkingGroups should only be set if one of the items is deselected - total_selected_items = len(self.objects_treeView.selectionModel().selectedIndexes()) + item = StandardItem(txt=child.name) + self.total_objects += 1 + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + remove_items = [] + self.get_all_removed_items(self.rootNode, scene, remove_items) + + for item in remove_items: + self.treeModel.takeRow(item.row()) + self.total_objects -= 1 + del item + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: + if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + continue + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.total_objects += 1 + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.objects_treeView.expandAll() + + def linked_objects_selection(self, selected, deselected): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) + is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' + ll = self.find_light_link_item(light_nm) + + if ll is None: + ll = scene.renderman.light_links.add() + ll.name = light_ob.name + ll.light_ob = light_ob + + if is_light_filter: + # linkingGroups should only be set if one of the items is deselected + total_selected_items = len(self.objects_treeView.selectionModel().selectedIndexes()) + + if total_selected_items == self.total_objects and light_props.linkingGroups != "": + light_props.linkingGroups = "" + light_ob.update_tag(refresh={'DATA'}) + elif total_selected_items != self.total_objects and light_props.linkingGroups == "": + light_props.linkingGroups = string_utils.sanitize_node_name(light_ob.name_full) + light_ob.update_tag(refresh={'DATA'}) + + for i in deselected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in ll.members: + if ob == member.ob_pointer: + do_add = False + break + + if do_add: + member = ll.members.add() + member.name = ob.name + member.ob_pointer = ob + member.illuminate = 'OFF' - if total_selected_items == self.total_objects and light_props.linkingGroups != "": - light_props.linkingGroups = "" - light_ob.update_tag(refresh={'DATA'}) - elif total_selected_items != self.total_objects and light_props.linkingGroups == "": - light_props.linkingGroups = string_utils.sanitize_node_name(light_ob.name_full) - light_ob.update_tag(refresh={'DATA'}) - - for i in deselected.indexes(): - item = self.objects_treeView.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_add = True - for member in ll.members: - if ob == member.ob_pointer: - do_add = False - break - - if do_add: - member = ll.members.add() - member.name = ob.name - member.ob_pointer = ob - member.illuminate = 'OFF' - - scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) - - for i in selected.indexes(): - item = self.objects_treeView.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_remove = False - idx = -1 - for i, member in enumerate(ll.members): - if ob == member.ob_pointer: - do_remove = True - idx = i - break - if do_remove: - member = ll.members.remove(idx) - - scene_utils.set_lightlinking_properties(ob, light_ob, '') + scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) + + for i in selected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_remove = False + idx = -1 + for i, member in enumerate(ll.members): + if ob == member.ob_pointer: + do_remove = True + idx = i + break + if do_remove: + member = ll.members.remove(idx) + + scene_utils.set_lightlinking_properties(ob, light_ob, '') class RENDERMAN_UL_LightLink_Light_List(bpy.types.UIList): @@ -643,16 +640,14 @@ def check_light_links(self, context): def invoke(self, context, event): - ''' - if rfb_qt and get_pref('rman_ui_framework') == 'QT': + if using_qt() and envconfig().getenv('RFB_DEVELOPER'): global __LIGHT_LINKING_WINDOW__ if sys.platform == "darwin": rfb_qt.run_with_timer(__LIGHT_LINKING_WINDOW__, LightLinkingQtWrapper) else: bpy.ops.wm.light_linking_qt_app_timed() - return {'RUNNING_MODAL'} - ''' + return {'RUNNING_MODAL'} wm = context.window_manager width = rfb_config['editor_preferences']['lightlink_editor']['width'] @@ -663,7 +658,8 @@ def invoke(self, context, event): classes = [ PRMAN_PT_Renderman_Open_Light_Linking, RENDERMAN_UL_LightLink_Light_List, - RENDERMAN_UL_LightLink_Object_List + RENDERMAN_UL_LightLink_Object_List, + LightLinkingQtAppTimed ] def register(): diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 92f2a0ae..56b5c989 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -5,309 +5,304 @@ from ...rfb_logger import rfb_log from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config -from ...rfb_utils.prefs_utils import get_pref +from ...rfb_utils.prefs_utils import get_pref, using_qt +from ...rman_ui import rfb_qt as rfb_qt +from ...rfb_utils.envconfig_utils import envconfig import bpy import re import sys +from PySide2 import QtCore, QtWidgets, QtGui -rfb_qt = None __TRACE_GROUPS_WINDOW__ = None -try: - from ...rman_ui import rfb_qt as rfb_qt -except: - pass - -if rfb_qt: - - from PySide2 import QtCore, QtWidgets, QtGui - class TraceGroupsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.trace_groups_qt_app_timed" - bl_label = "RenderMan Trace Sets Editor" - - def __init__(self): - super(TraceGroupsQtAppTimed, self).__init__() - - def execute(self, context): - self._window = TraceGroupsQtWrapper() - return super(TraceGroupsQtAppTimed, self).execute(context) - - class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - - class TraceGroupsQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(TraceGroupsQtWrapper, self).__init__() - - self.setWindowTitle('RenderMan Trace Groups') - self.resize(620, 475) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.addButton = QtWidgets.QPushButton(self) - self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) - self.addButton.setObjectName("addButton") - self.addButton.setText("+") - self.removeButton = QtWidgets.QPushButton(self) - self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) - self.removeButton.setObjectName("removeButton") - self.removeButton.setText("-") - - self.traceGroupObjects = QtWidgets.QTreeView(self) - self.traceGroupObjects.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.traceGroupObjects.setModel(self.treeModel) - - self.traceGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) - self.traceGroupObjects.setObjectName("traceGroupObjects") - self.traceGroupObjects.setSelectionMode( - QtWidgets.QAbstractItemView.MultiSelection - ) - - self.traceGroups = QtWidgets.QListWidget(self) - self.traceGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) - self.traceGroups.setObjectName("traceGroups") - - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) - self.label.setText("Trace Groups") - - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) - self.label_2.setText("Objects") - - self.refresh_btn = QtWidgets.QPushButton(self) - self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) - self.refresh_btn.setText("Refresh") - self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) - - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) - QtCore.QMetaObject.connectSlotsByName(self) - - self.addButton.clicked.connect(self.add_group) - self.removeButton.clicked.connect(self.remove_group) - self.refresh_btn.clicked.connect(self.refresh_group_objects) - - self.traceGroups.itemChanged.connect(self.trace_group_changed) - self.traceGroups.itemSelectionChanged.connect(self.trace_groups_index_changed) - self.traceGroupObjects.selectionModel().selectionChanged.connect(self.trace_group_objects_selection) - - self.refresh_groups() - self.refresh_group_objects() - - self.traceGroupObjects.expandAll() - - self.add_handlers() - - def closeEvent(self, event): - self.remove_handlers() - super(TraceGroupsQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Scene): - self.trace_groups_index_changed() - elif isinstance(dps_update.id, bpy.types.Object) or isinstance(dps_update.id, bpy.types.Collection): - self.refresh_groups() - self.refresh_group_objects() - - - def update(self): - idx = int(self.traceGroups.currentRow()) - self.addButton.setEnabled(True) - if self.traceGroups.count() < 1: - self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.enable_trace_group_objects(self.rootNode, enable=False) - self.removeButton.setEnabled(False) - else: - self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) - self.removeButton.setEnabled(True) - self.enable_trace_group_objects(self.rootNode, enable=True) - - super(TraceGroupsQtWrapper, self).update() - - def refresh_groups(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - self.traceGroups.clear() - for grp in rm.object_groups: - item = QtWidgets.QListWidgetItem(grp.name) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - self.traceGroups.addItem(item) - - if self.traceGroups.count() > 0: - self.traceGroups.setCurrentRow(rm.object_groups_index) - - def add_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.object_groups.add() - grp.name = 'traceGroup_%d' % len(rm.object_groups) - rm.object_groups_index = len(rm.object_groups)-1 - self.refresh_groups() - - def remove_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - index = rm.object_groups_index - group = rm.object_groups[index] - # get a list of all objects in this group - ob_list = [member.ob_pointer for member in group.members] - rm.object_groups.remove(index) - rm.object_groups_index -= 1 - - # now tell each object to update - for ob in ob_list: - ob.update_tag(refresh={'OBJECT'}) - - self.refresh_groups() - - def trace_group_changed(self, item): - idx = int(self.traceGroups.currentRow()) - - context = bpy.context - scene = context.scene - rm = scene.renderman - grp = rm.object_groups[idx] - grp.name = item.text() - self.label_2.setText("Objects (%s)" % item.text()) - - def find_item(self, standard_item, ob): - ''' - if standard_item.text() == ob.name: - return standard_item - - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - if item.hasChildren(): - return self.find_item(item, ob) - ''' - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - - return None - - def enable_trace_group_objects(self, standard_item, enable=True): - standard_item.setEnabled(enable) - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - item.setEnabled(enable) - if item.hasChildren(): - return self.enable_trace_group_objects(item, enable=enable) - - def refresh_group_objects(self): - idx = int(self.traceGroups.currentRow()) - enabled = True - if idx == -1: - enabled = False - self.label_2.setText("Objects (no group selected)") - context = bpy.context - scene = context.scene - rm = scene.renderman + +class TraceGroupsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.trace_groups_qt_app_timed" + bl_label = "RenderMan Trace Sets Editor" + + def __init__(self): + super(TraceGroupsQtAppTimed, self).__init__() + + def execute(self, context): + self._window = TraceGroupsQtWrapper() + return super(TraceGroupsQtAppTimed, self).execute(context) + +class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + +class TraceGroupsQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(TraceGroupsQtWrapper, self).__init__() + + self.setWindowTitle('RenderMan Trace Groups') + self.resize(620, 475) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + + self.traceGroupObjects = QtWidgets.QTreeView(self) + self.traceGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.traceGroupObjects.setModel(self.treeModel) + + self.traceGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) + self.traceGroupObjects.setObjectName("traceGroupObjects") + self.traceGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + + self.traceGroups = QtWidgets.QListWidget(self) + self.traceGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.traceGroups.setObjectName("traceGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) + self.label.setText("Trace Groups") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Objects") + + self.refresh_btn = QtWidgets.QPushButton(self) + self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) + self.refresh_btn.setText("Refresh") + self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + QtCore.QMetaObject.connectSlotsByName(self) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.refresh_btn.clicked.connect(self.refresh_group_objects) + + self.traceGroups.itemChanged.connect(self.trace_group_changed) + self.traceGroups.itemSelectionChanged.connect(self.trace_groups_index_changed) + self.traceGroupObjects.selectionModel().selectionChanged.connect(self.trace_group_objects_selection) + + self.refresh_groups() + self.refresh_group_objects() + + self.traceGroupObjects.expandAll() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(TraceGroupsQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Scene): + self.trace_groups_index_changed() + elif isinstance(dps_update.id, bpy.types.Object) or isinstance(dps_update.id, bpy.types.Collection): + self.refresh_groups() + self.refresh_group_objects() + + + def update(self): + idx = int(self.traceGroups.currentRow()) + self.addButton.setEnabled(True) + if self.traceGroups.count() < 1: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.enable_trace_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.removeButton.setEnabled(True) + self.enable_trace_group_objects(self.rootNode, enable=True) + + super(TraceGroupsQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.traceGroups.clear() + for grp in rm.object_groups: + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.traceGroups.addItem(item) + + if self.traceGroups.count() > 0: + self.traceGroups.setCurrentRow(rm.object_groups_index) - self.treeModel.clear() - self.rootNode = self.treeModel.invisibleRootItem() - - def add_children(root_item, ob): - for child in ob.children: - item = self.find_item(root_item, child) - if not item: - item = StandardItem(txt=child.name) - root_item.appendRow(item) - if len(child.children) > 0: - add_children(item, child) + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.object_groups.add() + grp.name = 'traceGroup_%d' % len(rm.object_groups) + rm.object_groups_index = len(rm.object_groups)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.object_groups_index + group = rm.object_groups[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.object_groups.remove(index) + rm.object_groups_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'OBJECT'}) + + self.refresh_groups() + + def trace_group_changed(self, item): + idx = int(self.traceGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.object_groups[idx] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + + def find_item(self, standard_item, ob): + ''' + if standard_item.text() == ob.name: + return standard_item + + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + if item.hasChildren(): + return self.find_item(item, ob) + ''' + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + + return None + + def enable_trace_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.enable_trace_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.traceGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + context = bpy.context + scene = context.scene + rm = scene.renderman + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: + item = self.find_item(root_item, child) + if not item: + item = StandardItem(txt=child.name) + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: - root_parents = [ob for ob in scene.objects if ob.parent is None] - for ob in root_parents: + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.traceGroupObjects.expandAll() + if idx != -1: + self.trace_groups_index_changed() + + def trace_groups_index_changed(self): + idx = int(self.traceGroups.currentRow()) + current_item = self.traceGroups.currentItem() + if current_item: + self.label_2.setText("Objects (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.object_groups_index = idx + + group_index = rm.object_groups_index + object_groups = rm.object_groups + object_group = object_groups[group_index] + + selected_items = QtCore.QItemSelection() + for member in object_group.members: + ob = member.ob_pointer + if ob is None: + continue + item = self.find_item(self.rootNode, ob) + if item: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - item = self.find_item(self.rootNode, ob) - if not item: - item = StandardItem(txt=ob.name) - self.rootNode.appendRow(item) - if len(ob.children) > 0: - add_children(item, ob) - - self.traceGroupObjects.expandAll() - if idx != -1: - self.trace_groups_index_changed() + def trace_group_objects_selection(self, selected, deselected): + context = bpy.context + scene = context.scene + rm = scene.renderman - def trace_groups_index_changed(self): - idx = int(self.traceGroups.currentRow()) - current_item = self.traceGroups.currentItem() - if current_item: - self.label_2.setText("Objects (%s)" % current_item.text()) - else: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.object_groups_index = idx - - group_index = rm.object_groups_index - object_groups = rm.object_groups - object_group = object_groups[group_index] - - selected_items = QtCore.QItemSelection() - for member in object_group.members: - ob = member.ob_pointer - if ob is None: - continue - item = self.find_item(self.rootNode, ob) - if item: - idx = self.treeModel.indexFromItem(item) - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - - def trace_group_objects_selection(self, selected, deselected): - context = bpy.context - scene = context.scene - rm = scene.renderman - - group_index = rm.object_groups_index - object_groups = rm.object_groups - if group_index not in range(0, len(object_groups)): - return - object_group = object_groups[group_index] - - for i in selected.indexes(): - item = self.traceGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_add = True - for member in object_group.members: - if ob == member.ob_pointer: - do_add = False - break - if do_add: - ob_in_group = object_group.members.add() - ob_in_group.name = ob.name - ob_in_group.ob_pointer = ob - ob.update_tag(refresh={'OBJECT'}) + group_index = rm.object_groups_index + object_groups = rm.object_groups + if group_index not in range(0, len(object_groups)): + return + object_group = object_groups[group_index] + + for i in selected.indexes(): + item = self.traceGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in object_group.members: + if ob == member.ob_pointer: + do_add = False + break + if do_add: + ob_in_group = object_group.members.add() + ob_in_group.name = ob.name + ob_in_group.ob_pointer = ob + ob.update_tag(refresh={'OBJECT'}) class RENDERMAN_UL_Object_Group_List(bpy.types.UIList): @@ -463,8 +458,7 @@ def check_tracegroups(self, context): def invoke(self, context, event): - ''' - if rfb_qt and get_pref('rman_ui_framework') == 'QT': + if using_qt() and envconfig().getenv('RFB_DEVELOPER'): global __TRACE_GROUPS_WINDOW__ if sys.platform == "darwin": rfb_qt.run_with_timer(__TRACE_GROUPS_WINDOW__, TraceGroupsQtWrapper) @@ -472,7 +466,6 @@ def invoke(self, context, event): bpy.ops.wm.trace_groups_qt_app_timed() return {'RUNNING_MODAL'} - ''' wm = context.window_manager width = rfb_config['editor_preferences']['tracesets_editor']['width'] @@ -482,12 +475,9 @@ def invoke(self, context, event): classes = [ PRMAN_OT_Renderman_Open_Groups_Editor, - RENDERMAN_UL_Object_Group_List + RENDERMAN_UL_Object_Group_List, + TraceGroupsQtAppTimed ] - -if rfb_qt: - classes.append(TraceGroupsQtAppTimed) - def register(): from ...rfb_utils import register_utils diff --git a/rman_presets/qt_app.py b/rman_presets/qt_app.py deleted file mode 100644 index f2c13fed..00000000 --- a/rman_presets/qt_app.py +++ /dev/null @@ -1,85 +0,0 @@ -try: - from ..rman_ui import rfb_qt -except: - raise - -import sys -import bpy - -from ..rfb_logger import rfb_log -from . import core as bl_pb_core - -__PRESET_BROWSER_WINDOW__ = None - -class PresetBrowserQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.rpb_qt_app_timed" - bl_label = "RenderManPreset Browser" - - def __init__(self): - super(PresetBrowserQtAppTimed, self).__init__() - - def execute(self, context): - global __PRESET_BROWSER_WINDOW__ - __PRESET_BROWSER_WINDOW__ = PresetBrowserWrapper() - self._window = __PRESET_BROWSER_WINDOW__ - return super(PresetBrowserQtAppTimed, self).execute(context) - -class PresetBrowserWrapper(rfb_qt.RmanQtWrapper): - - def __init__(self): - super(PresetBrowserWrapper, self).__init__() - # import here because we will crash Blender - # when we try to import it globally - import rman_utils.rman_assets.ui as rui - - self.resize(1024, 1024) - self.setWindowTitle('RenderMan Preset Browser') - - if sys.platform != "darwin": - bg_role = self.backgroundRole() - plt = self.palette() - bg_color = plt.color(bg_role) - bg_color.setRgb(70, 70, 70) - plt.setColor(bg_role, bg_color) - self.setPalette(plt) - - self.hostPrefs = bl_pb_core.get_host_prefs() - self.ui = rui.Ui(self.hostPrefs, parent=self) - self.setLayout(self.ui.topLayout) - self.show() # Show window - - def closeEvent(self, event): - self.hostPrefs.saveAllPrefs() - event.accept() - -class PRMAN_OT_Renderman_Presets_Editor(bpy.types.Operator): - bl_idname = "renderman.rman_open_presets_editor" - bl_label = "PresetBrowser" - - def execute(self, context): - - global __PRESET_BROWSER_WINDOW__ - if __PRESET_BROWSER_WINDOW__ and __PRESET_BROWSER_WINDOW__.isVisible(): - return {'FINISHED'} - - if sys.platform == "darwin": - __PRESET_BROWSER_WINDOW__ = rfb_qt.run_with_timer(__PRESET_BROWSER_WINDOW__, PresetBrowserWrapper) - else: - bpy.ops.wm.rpb_qt_app_timed() - - return {'RUNNING_MODAL'} - -classes = [ - PRMAN_OT_Renderman_Presets_Editor, - PresetBrowserQtAppTimed -] - -def register(): - from ..rfb_utils import register_utils - - register_utils.rman_register_classes(classes) - -def unregister(): - from ..rfb_utils import register_utils - - register_utils.rman_unregister_classes(classes) \ No newline at end of file diff --git a/rman_presets/ui.py b/rman_presets/ui.py index 8e3de1b4..2023e8c8 100644 --- a/rman_presets/ui.py +++ b/rman_presets/ui.py @@ -23,27 +23,73 @@ # # ##### END MIT LICENSE BLOCK ##### -from ..rfb_utils.prefs_utils import get_pref, get_addon_prefs +from ..rfb_utils.prefs_utils import get_pref, get_addon_prefs, using_qt from ..rfb_logger import rfb_log from ..rman_config import __RFB_CONFIG_DICT__ as rfb_config +from ..rman_ui import rfb_qt # for panel icon from .. import rfb_icons from . import icons as rpb_icons import bpy +import sys from .properties import RendermanPreset, RendermanPresetCategory from bpy.props import * # for previews of assets from . import icons from . import rmanAssetsBlender as rab +from . import core as bl_pb_core from rman_utils.rman_assets import core as ra from rman_utils.rman_assets.common.exceptions import RmanAssetError from bpy.props import StringProperty, IntProperty import os +__PRESET_BROWSER_WINDOW__ = None + +class PresetBrowserQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.rpb_qt_app_timed" + bl_label = "RenderManPreset Browser" + + def __init__(self): + super(PresetBrowserQtAppTimed, self).__init__() + + def execute(self, context): + global __PRESET_BROWSER_WINDOW__ + __PRESET_BROWSER_WINDOW__ = PresetBrowserWrapper() + self._window = __PRESET_BROWSER_WINDOW__ + return super(PresetBrowserQtAppTimed, self).execute(context) + +class PresetBrowserWrapper(rfb_qt.RmanQtWrapper): + + def __init__(self): + super(PresetBrowserWrapper, self).__init__() + # import here because we will crash Blender + # when we try to import it globally + import rman_utils.rman_assets.ui as rui + + self.resize(1024, 1024) + self.setWindowTitle('RenderMan Preset Browser') + + if sys.platform != "darwin": + bg_role = self.backgroundRole() + plt = self.palette() + bg_color = plt.color(bg_role) + bg_color.setRgb(70, 70, 70) + plt.setColor(bg_role, bg_color) + self.setPalette(plt) + + self.hostPrefs = bl_pb_core.get_host_prefs() + self.ui = rui.Ui(self.hostPrefs, parent=self) + self.setLayout(self.ui.topLayout) + self.show() # Show window + + def closeEvent(self, event): + self.hostPrefs.saveAllPrefs() + event.accept() + # panel for the toolbar of node editor class PRMAN_PT_Renderman_Presets_UI_Panel(bpy.types.Panel): @@ -449,6 +495,18 @@ def __init__(self): def invoke(self, context, event): + if using_qt(): + global __PRESET_BROWSER_WINDOW__ + if __PRESET_BROWSER_WINDOW__ and __PRESET_BROWSER_WINDOW__.isVisible(): + return {'FINISHED'} + + if sys.platform == "darwin": + __PRESET_BROWSER_WINDOW__ = rfb_qt.run_with_timer(__PRESET_BROWSER_WINDOW__, PresetBrowserWrapper) + else: + bpy.ops.wm.rpb_qt_app_timed() + + return {'RUNNING_MODAL'} + self.load_categories(context) self.load_presets(context) @@ -474,7 +532,9 @@ def rman_presets_object_menu(self, context): VIEW3D_MT_renderman_presets_object_context_menu, PRMAN_MT_renderman_preset_ops_menu, RENDERMAN_UL_Presets_Categories_List, - RENDERMAN_UL_Presets_Preset_List + RENDERMAN_UL_Presets_Preset_List, + PRMAN_OT_Renderman_Presets_Editor, + PresetBrowserQtAppTimed ] @@ -482,16 +542,6 @@ def register(): from ..rfb_utils import register_utils register_utils.rman_register_classes(classes) - - if get_pref('rman_ui_framework') != 'QT': - register_utils.rman_register_class(PRMAN_OT_Renderman_Presets_Editor) - else: - try: - from PySide2 import QtCore, QtWidgets - except: - # can't find PySide2, load old preset browser - register_utils.rman_register_class(PRMAN_OT_Renderman_Presets_Editor) - bpy.types.VIEW3D_MT_add.prepend(rman_presets_object_menu) bpy.types.VIEW3D_MT_object_context_menu.prepend(rman_presets_object_menu) @@ -502,4 +552,4 @@ def unregister(): bpy.types.VIEW3D_MT_add.remove(rman_presets_object_menu) bpy.types.VIEW3D_MT_object_context_menu.remove(rman_presets_object_menu) - register_utils.rman_unregister_class(PRMAN_OT_Renderman_Presets_Editor) \ No newline at end of file + register_utils.rman_unregister_classes(classes) \ No newline at end of file diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index 3c0ef257..1ab8e60c 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -451,17 +451,9 @@ def draw_render_stats(self): return def register(): - if prefs_utils.get_pref('rman_ui_framework') == 'QT': - try: - from . import operators - operators.register() - except: - pass + from . import operators + operators.register() def unregister(): - if prefs_utils.get_pref('rman_ui_framework') == 'QT': - try: - from . import operators - operators.unregister() - except: - pass \ No newline at end of file + from . import operators + operators.unregister() \ No newline at end of file diff --git a/rman_stats/operators.py b/rman_stats/operators.py index 53630c1d..ef07aad1 100644 --- a/rman_stats/operators.py +++ b/rman_stats/operators.py @@ -1,8 +1,4 @@ -try: - from ..rman_ui import rfb_qt -except: - raise - +from ..rman_ui import rfb_qt import bpy import sys from ..rfb_logger import rfb_log diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index a858ce0d..3ccec498 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -7,16 +7,96 @@ from ..rfb_utils import shadergraph_utils from ..rfb_utils import scene_utils from ..rfb_utils import object_utils -from ..rfb_utils.prefs_utils import get_pref +from ..rfb_utils.prefs_utils import get_pref, using_qt from ..rfb_logger import rfb_log from ..rman_config import __RFB_CONFIG_DICT__ as rfb_config +from ..rman_constants import RFB_HELP_URL from .. import rman_render from rman_utils.txmanager import txparams from rman_utils import txmanager as txmngr from .. import rfb_icons +from ..rman_ui import rfb_qt +import sys +import hashlib import os import uuid +__QT_LOADED__ = True +__TXMANAGER_WINDOW__ = None + +class TxManagerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.txm_qt_app_timed" + bl_label = "Texture Manager" + + def __init__(self): + super(TxManagerQtAppTimed, self).__init__() + + def execute(self, context): + self._window = create_widget() + return super(TxManagerQtAppTimed, self).execute(context) + +def parse_scene(): + from ..rfb_utils import texture_utils + bl_scene = bpy.context.scene + mgr = texture_utils.get_txmanager().txmanager + mgr.reset() + texture_utils.parse_for_textures(bl_scene) + +def _append_to_tx_list(file_path_list): + """Called by the txmanager when extra files are added to the scene list. + """ + from ..rfb_utils import texture_utils + bl_scene = bpy.context.scene + txmgr = texture_utils.get_txmanager().txmanager + texture_utils.parse_for_textures(bl_scene) + for fpath in file_path_list: + # Pass None as the nodeID and a hash will be generated. + texid = hashlib.sha1(fpath.encode('utf-8')).hexdigest() + txmgr.add_texture(texid, fpath) + txmgr.update_ui_list() + # make sure to restart the queue. + txmgr.txmake_all(start_queue=True, blocking=False) + +def help_func(url): + bpy.ops.wm.url_open(url = RFB_HELP_URL) + +def create_widget(): + global __TXMANAGER_WINDOW__ + if not __TXMANAGER_WINDOW__: + import rman_utils.txmanager.ui as rui + from ..rfb_utils import texture_utils + mgr = texture_utils.get_txmanager().txmanager + __TXMANAGER_WINDOW__ = rui.TxManagerUI(None, txmanager=mgr, + parse_scene_func=parse_scene, + append_tx_func=_append_to_tx_list, + help_func=help_func) + mgr.ui = __TXMANAGER_WINDOW__ + return __TXMANAGER_WINDOW__ + +class PRMAN_OT_TxManager_Qt(bpy.types.Operator): + bl_idname = "rman_txmgr_list.open_txmanager" + bl_label = "Texture Manager" + + nodeID: bpy.props.StringProperty(default='') + + def execute(self, context): + from ..rfb_utils import texture_utils + global __TXMANAGER_WINDOW__ + if __TXMANAGER_WINDOW__ and __TXMANAGER_WINDOW__.isVisible(): + return {'FINISHED'} + + if sys.platform == "darwin": + rfb_qt.run_with_timer(__TXMANAGER_WINDOW__, create_widget) + else: + bpy.ops.wm.txm_qt_app_timed() + mgr = texture_utils.get_txmanager().txmanager + mgr.update_ui_list() + if self.nodeID: + txfile = mgr.get_txfile_from_id(self.nodeID) + mgr.ui.select_txfile(txfile) + + return {'RUNNING_MODAL'} + class TxFileItem(PropertyGroup): """UIList item representing a TxFile""" @@ -701,6 +781,24 @@ def __init__(self): self.event = None def invoke(self, context, event): + if using_qt(): + global __TXMANAGER_WINDOW__ + if __TXMANAGER_WINDOW__ and __TXMANAGER_WINDOW__.isVisible(): + return {'FINISHED'} + + if sys.platform == "darwin": + rfb_qt.run_with_timer(__TXMANAGER_WINDOW__, create_widget) + else: + bpy.ops.wm.txm_qt_app_timed() + mgr = texture_utils.get_txmanager().txmanager + mgr.update_ui_list() + if self.nodeID: + txfile = mgr.get_txfile_from_id(self.nodeID) + mgr.ui.select_txfile(txfile) + + return {'RUNNING_MODAL'} + + if self.properties.nodeID != '': for i, item in enumerate(context.scene.rman_txmgr_list): if item.nodeID == self.properties.nodeID: @@ -768,21 +866,15 @@ def index_updated(self, context): PRMAN_OT_Renderman_txmanager_add_texture, PRMAN_OT_Renderman_txmanager_refresh, PRMAN_PT_Renderman_txmanager_list, - PRMAN_OT_Renderman_txmanager_remove_texture + PRMAN_OT_Renderman_txmanager_remove_texture, + PRMAN_OT_TxManager_Qt, + PRMAN_OT_Renderman_open_txmanager, + TxManagerQtAppTimed ] def register(): - from ..rfb_utils import register_utils - - if get_pref('rman_ui_framework') == 'QT': - try: - from . import rman_ui_txmanager_qt - rman_ui_txmanager_qt.register() - except: - register_utils.rman_register_class(PRMAN_OT_Renderman_open_txmanager) - else: - register_utils.rman_register_class(PRMAN_OT_Renderman_open_txmanager) + from ..rfb_utils import register_utils register_utils.rman_register_classes(classes) @@ -796,11 +888,5 @@ def unregister(): del bpy.types.Scene.rman_txmgr_list_index from ..rfb_utils import register_utils - - if get_pref('rman_ui_framework') == 'QT': - try: - from . import rman_ui_txmanager_qt - rman_ui_txmanager_qt.unregister() - except: - pass + register_utils.rman_unregister_classes(classes) \ No newline at end of file diff --git a/rman_ui/rman_ui_txmanager_qt.py b/rman_ui/rman_ui_txmanager_qt.py deleted file mode 100644 index a4a39918..00000000 --- a/rman_ui/rman_ui_txmanager_qt.py +++ /dev/null @@ -1,104 +0,0 @@ -__QT_LOADED__ = False - -try: - from ..rman_ui import rfb_qt -except: - raise - -import sys -import bpy -import hashlib - -from ..rfb_logger import rfb_log -from ..rman_constants import RFB_HELP_URL - -__QT_LOADED__ = True -__TXMANAGER_WINDOW__ = None - -class TxManagerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.txm_qt_app_timed" - bl_label = "Texture Manager" - - def __init__(self): - super(TxManagerQtAppTimed, self).__init__() - - def execute(self, context): - self._window = create_widget() - return super(TxManagerQtAppTimed, self).execute(context) - -def parse_scene(): - from ..rfb_utils import texture_utils - bl_scene = bpy.context.scene - mgr = texture_utils.get_txmanager().txmanager - mgr.reset() - texture_utils.parse_for_textures(bl_scene) - -def _append_to_tx_list(file_path_list): - """Called by the txmanager when extra files are added to the scene list. - """ - from ..rfb_utils import texture_utils - bl_scene = bpy.context.scene - txmgr = texture_utils.get_txmanager().txmanager - texture_utils.parse_for_textures(bl_scene) - for fpath in file_path_list: - # Pass None as the nodeID and a hash will be generated. - texid = hashlib.sha1(fpath.encode('utf-8')).hexdigest() - txmgr.add_texture(texid, fpath) - txmgr.update_ui_list() - # make sure to restart the queue. - txmgr.txmake_all(start_queue=True, blocking=False) - -def help_func(url): - bpy.ops.wm.url_open(url = RFB_HELP_URL) - -def create_widget(): - global __TXMANAGER_WINDOW__ - if not __TXMANAGER_WINDOW__: - import rman_utils.txmanager.ui as rui - from ..rfb_utils import texture_utils - mgr = texture_utils.get_txmanager().txmanager - __TXMANAGER_WINDOW__ = rui.TxManagerUI(None, txmanager=mgr, - parse_scene_func=parse_scene, - append_tx_func=_append_to_tx_list, - help_func=help_func) - mgr.ui = __TXMANAGER_WINDOW__ - return __TXMANAGER_WINDOW__ - -class PRMAN_OT_TxManager_Qt(bpy.types.Operator): - bl_idname = "rman_txmgr_list.open_txmanager" - bl_label = "Texture Manager" - - nodeID: bpy.props.StringProperty(default='') - - def execute(self, context): - from ..rfb_utils import texture_utils - global __TXMANAGER_WINDOW__ - if __TXMANAGER_WINDOW__ and __TXMANAGER_WINDOW__.isVisible(): - return {'FINISHED'} - - if sys.platform == "darwin": - rfb_qt.run_with_timer(__TXMANAGER_WINDOW__, create_widget) - else: - bpy.ops.wm.txm_qt_app_timed() - mgr = texture_utils.get_txmanager().txmanager - mgr.update_ui_list() - if self.nodeID: - txfile = mgr.get_txfile_from_id(self.nodeID) - mgr.ui.select_txfile(txfile) - - return {'RUNNING_MODAL'} - -classes = [ - PRMAN_OT_TxManager_Qt, - TxManagerQtAppTimed -] - -def register(): - from ..rfb_utils import register_utils - - register_utils.rman_register_classes(classes) - -def unregister(): - from ..rfb_utils import register_utils - - register_utils.rman_unregister_classes(classes) \ No newline at end of file diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index a067e0ef..07ea2125 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -270,7 +270,7 @@ def draw(self, context): if light_props.renderman_light_role not in {'RMAN_LIGHTFILTER', 'RMAN_LIGHT'}: return selected_objects = context.selected_objects - if selected_objects: + if not using_qt() and not envconfig().getenv('RFB_DEVELOPER') and selected_objects: layout.context_pointer_set('light_ob', active_light) if not rm.invert_light_linking: layout.separator() diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index b153ff21..3fb7872a 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -266,7 +266,7 @@ def draw(self, context): scene = context.scene rm = scene.renderman rr = RmanRender.get_rman_render() - if hasattr(bpy.types, bpy.ops.renderman.rman_open_stats.idname()): + if prefs_utils.using_qt(): layout.separator() layout.operator("renderman.rman_open_stats") if rr.stats_mgr.is_connected(): From 9dc1823ac56cdfdba8fe929840206e8698d724e1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Nov 2022 05:10:16 -0800 Subject: [PATCH 214/278] Remove an unnecessary class from rman_ui_txmanager. --- rman_ui/rman_ui_txmanager.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index 3ccec498..70e16e75 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -72,32 +72,6 @@ def create_widget(): help_func=help_func) mgr.ui = __TXMANAGER_WINDOW__ return __TXMANAGER_WINDOW__ - -class PRMAN_OT_TxManager_Qt(bpy.types.Operator): - bl_idname = "rman_txmgr_list.open_txmanager" - bl_label = "Texture Manager" - - nodeID: bpy.props.StringProperty(default='') - - def execute(self, context): - from ..rfb_utils import texture_utils - global __TXMANAGER_WINDOW__ - if __TXMANAGER_WINDOW__ and __TXMANAGER_WINDOW__.isVisible(): - return {'FINISHED'} - - if sys.platform == "darwin": - rfb_qt.run_with_timer(__TXMANAGER_WINDOW__, create_widget) - else: - bpy.ops.wm.txm_qt_app_timed() - mgr = texture_utils.get_txmanager().txmanager - mgr.update_ui_list() - if self.nodeID: - txfile = mgr.get_txfile_from_id(self.nodeID) - mgr.ui.select_txfile(txfile) - - return {'RUNNING_MODAL'} - - class TxFileItem(PropertyGroup): """UIList item representing a TxFile""" @@ -867,7 +841,6 @@ def index_updated(self, context): PRMAN_OT_Renderman_txmanager_refresh, PRMAN_PT_Renderman_txmanager_list, PRMAN_OT_Renderman_txmanager_remove_texture, - PRMAN_OT_TxManager_Qt, PRMAN_OT_Renderman_open_txmanager, TxManagerQtAppTimed ] From 6c4d109e17f4d660ab5bd46ae1f73fc37a27914e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Nov 2022 06:10:10 -0800 Subject: [PATCH 215/278] Fix for item selection in the Qt version of light linking UI. Was not iterating through the QTreeView correctly. --- .../rman_operators_editors_lightlink.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index b6726900..52be8b23 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -167,7 +167,14 @@ def find_light_link_item(self, light_nm=''): if ll.light_ob.name == light_nm: light_link_item = ll break - return light_link_item + return light_link_item + + def get_all_object_items(self, standard_item, items): + for i in range(standard_item.rowCount()): + item = standard_item.child(i) + items.append(item) + if item.rowCount() > 0: + self.get_alget_all_object_itemsl_items(item, items) def lights_index_changed(self): idx = int(self.lights_treeView.currentRow()) @@ -187,23 +194,21 @@ def lights_index_changed(self): light_link_item = self.find_light_link_item(light_nm) selected_items = QtCore.QItemSelection() + items = [] + self.get_all_object_items(self.rootNode, items) if light_link_item is None: if not object_utils.is_light_filter(light_ob): light_link_item = scene.renderman.light_links.add() light_link_item.name = light_ob.name light_link_item.light_ob = light_ob - - for i in range(0, self.rootNode.rowCount()): - item = self.rootNode.child(i) + + for item in items: idx = self.treeModel.indexFromItem(item) selection_range = QtCore.QItemSelectionRange(idx) selected_items.append(selection_range) else: - for i in range(0, self.rootNode.rowCount()): - item = self.rootNode.child(i) - if not item: - continue + for item in items: idx = self.treeModel.indexFromItem(item) ob_nm = item.text() found = False @@ -253,7 +258,7 @@ def refresh_linked_objects(self): def add_children(root_item, ob): for child in ob.children: - if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + if child.type in ['CAMERA', 'LIGHT', 'ARMATURE']: continue item = self.find_item(root_item, child) if not item: From 64f8a7c1f65c4f8e013b9cf3f54d7456fe7e1c93 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Nov 2022 06:28:44 -0800 Subject: [PATCH 216/278] Fix typo. get_alget_all_object_itemsl_items -> get_all_object_items --- .../rman_operators_editors/rman_operators_editors_lightlink.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 52be8b23..a0cdaf52 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -174,7 +174,7 @@ def get_all_object_items(self, standard_item, items): item = standard_item.child(i) items.append(item) if item.rowCount() > 0: - self.get_alget_all_object_itemsl_items(item, items) + self.get_all_object_items(item, items) def lights_index_changed(self): idx = int(self.lights_treeView.currentRow()) From e52b55ba6f6a7b53338af524311c006515e01db8 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 23 Nov 2022 18:47:48 -0800 Subject: [PATCH 217/278] Show Pxr light icon beside each light in the lights list for the Qt light linking UI. --- .../rman_operators_editors_lightlink.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index a0cdaf52..5e09c6db 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -12,7 +12,9 @@ from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config from ...rman_ui import rfb_qt as rfb_qt +from ...rman_constants import RFB_ADDON_PATH import bpy +import os import re import sys from PySide2 import QtCore, QtWidgets, QtGui @@ -148,6 +150,12 @@ def refresh_lights(self): for nm in all_lights: item = QtWidgets.QListWidgetItem(nm) + ob = scene.objects[nm] + light_shader_name = ob.data.renderman.get_light_node_name() + icon_path = os.path.join(RFB_ADDON_PATH, 'rfb_icons/out_%s.png' % light_shader_name) + if os.path.exists(icon_path): + icon = QtGui.QIcon(icon_path) + item.setIcon(icon) item.setFlags(item.flags()) self.lights_treeView.addItem(item) From c427e7077d79d4897932dafb2b60a0e6e2d4922e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 29 Nov 2022 01:22:36 -0800 Subject: [PATCH 218/278] The "it" inspector was displaying the wrong min/max samples when in IPR. --- rfb_utils/display_utils.py | 8 ++++++-- rman_scene.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index c520dcc5..67c19a97 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -669,7 +669,7 @@ def get_dspy_dict(rman_scene, expandTokens=True, include_holdouts=True): return dspys_dict -def make_dspy_info(scene): +def make_dspy_info(scene, is_interactive=False): """ Create some render parameter from scene and pass it to image tool. @@ -679,6 +679,7 @@ def make_dspy_info(scene): Arguments: scene (bpy.types.Scene) - Blender scene object + is_interactive (bool) - True if we are in IPR Returns: (str) - a string with the display notes to give to "it" @@ -697,7 +698,10 @@ def make_dspy_info(scene): dspy_notes = "Render start:\t%s\r\r" % ts dspy_notes += "Integrator:\t%s\r\r" % integrator_nm - dspy_notes += "Samples:\t%d - %d\r" % (rm.hider_minSamples, rm.hider_maxSamples) + if is_interactive: + dspy_notes += "Samples:\t%d - %d\r" % (rm.ipr_hider_minSamples, rm.ipr_hider_maxSamples) + else: + dspy_notes += "Samples:\t%d - %d\r" % (rm.hider_minSamples, rm.hider_maxSamples) dspy_notes += "Pixel Variance:\t%f\r\r" % rm.ri_pixelVariance # moved this in front of integrator check. Was called redundant in diff --git a/rman_scene.py b/rman_scene.py index 07ed4ff9..fbe01bc5 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -1554,7 +1554,7 @@ def export_displays(self): display.params.Inherit(dspydriver_params) display.params.SetString("mode", channels) if display_driver == "it": - dspy_info = display_utils.make_dspy_info(self.bl_scene) + dspy_info = display_utils.make_dspy_info(self.bl_scene, self.is_interactive) port = self.rman_render.it_port dspy_callback = "dspyRender" if self.is_interactive: From c7707b73dd523953a41d84e3f9ae531e4e5f2742 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 1 Dec 2022 03:30:34 -0800 Subject: [PATCH 219/278] Change the clapboard icon in the viewport to now be a dropdown menu, allowing the user to IPR into either the viewport or "it". --- rman_operators/rman_operators_render.py | 2 +- rman_ui/rman_ui_viewport.py | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/rman_operators/rman_operators_render.py b/rman_operators/rman_operators_render.py index 2118a216..53710f54 100644 --- a/rman_operators/rman_operators_render.py +++ b/rman_operators/rman_operators_render.py @@ -192,7 +192,7 @@ class PRMAN_OT_StartInteractive(bpy.types.Operator): '''''' bl_idname = "renderman.start_ipr" bl_label = "Start Interactive Rendering" - bl_description = "Start Interactive Rendering" + bl_description = "Start IPR and render to the viewport" bl_options = {'INTERNAL'} render_to_it: bpy.props.BoolProperty(default=False) diff --git a/rman_ui/rman_ui_viewport.py b/rman_ui/rman_ui_viewport.py index 8bf5ece4..5a4cfd5e 100644 --- a/rman_ui/rman_ui_viewport.py +++ b/rman_ui/rman_ui_viewport.py @@ -792,6 +792,23 @@ def invoke(self, context, event): self.crop_handler.crop_windowing = True return {'RUNNING_MODAL'} +class PRMAN_MT_Viewport_Render_Menu(Menu): + bl_label = "Render Viewport Menu" + bl_idname = "PRMAN_MT_Viewport_Render_Menu" + + @classmethod + def poll(cls, context): + return context.engine == "PRMAN_RENDER" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_DEFAULT' + op = layout.operator('renderman.start_ipr', text='IPR to Viewport', icon='BLENDER') + op.render_to_it = False + rman_icon = rfb_icons.get_icon('rman_it') + op = layout.operator('renderman.start_ipr', text='IPR to it', icon_value=rman_icon.icon_id) + op.render_to_it = True + def draw_rman_viewport_props(self, context): layout = self.layout scene = context.scene @@ -850,9 +867,7 @@ def draw_rman_viewport_props(self, context): #rman_render.stop_render() rman_render.del_bl_engine() rman_rerender_controls = rfb_icons.get_icon("rman_ipr_on") - op = row.operator('renderman.start_ipr', text="", - icon_value=rman_rerender_controls.icon_id) - op.render_to_it = False + row.menu('PRMAN_MT_Viewport_Render_Menu', text='', icon_value=rman_rerender_controls.icon_id) row.popover(panel="PRMAN_PT_Viewport_Options", text="") @@ -919,7 +934,8 @@ def draw(self, context): PRMAN_OT_Viewport_CropWindow_Reset, PRMAN_OT_Viewport_Cropwindow, PRMAN_OT_Viewport_Enhance, - PRMAN_PT_Viewport_Options + PRMAN_PT_Viewport_Options, + PRMAN_MT_Viewport_Render_Menu ] def register(): From 91db8ad73141d3c88fd4cc5534c1831a1b81a3ce Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 13 Dec 2022 04:43:05 -0800 Subject: [PATCH 220/278] A couple of fixes when using the "use blender compositor option". * make sure we only use our render_pre and render_post handlers when the current render engine is prman * if we're not using RenderMan displays, make sure that AOVs are written to the selected image_settings.file_format. --- rfb_utils/display_utils.py | 9 +++++- rfb_utils/scene_utils.py | 4 +++ rman_handlers/__init__.py | 24 ++++++++++---- rman_render.py | 66 ++++++++++++++++++++++++++------------ 4 files changed, 74 insertions(+), 29 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 67c19a97..6cd5f2da 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -15,6 +15,7 @@ __RMAN_TO_BLENDER__ = { 'tiff': 'TIFF', 'targa': 'TARGA', 'openexr':'OPEN_EXR', 'png':'PNG'} def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False, no_ext=False): + dspy_info = dict() view_layer = bpy.context.view_layer rm_rl = None if view_layer.renderman.use_renderman: @@ -40,7 +41,13 @@ def get_beauty_filepath(bl_scene, use_blender_frame=False, expand_tokens=False, filePath = string_utils.expand_string(filePath, display=display_driver, asFilePath=True) - return filePath + dspy_info['filePath'] = filePath + dspy_info['display_driver'] = display_driver + return dspy_info + +def using_rman_displays(): + view_layer = bpy.context.view_layer + return view_layer.renderman.use_renderman def _default_dspy_params(): d = {} diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index 6c7c6504..b80b0983 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -2,6 +2,7 @@ from . import object_utils from . import prefs_utils from . import string_utils +from . import display_utils from ..rfb_logger import rfb_log import bpy import sys @@ -60,6 +61,9 @@ def should_use_bl_compositor(bl_scene): if not bpy.app.background: return (rm.render_into == 'blender') + if not display_utils.using_rman_displays(): + return True + if not rm.use_bl_compositor: # explicitiy turned off return False diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index e3b93f61..d4bcd468 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -62,17 +62,24 @@ def render_pre(bl_scene): from ..rfb_utils import display_utils from ..rfb_utils import scene_utils + if bl_scene.render.engine != 'PRMAN_RENDER': + return + __ORIGINAL_BL_FILEPATH__ = bl_scene.render.filepath __ORIGINAL_BL_FILE_FORMAT__ = bl_scene.render.image_settings.file_format write_comp = scene_utils.should_use_bl_compositor(bl_scene) - if write_comp: - filePath = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True, no_ext=True) - bl_scene.render.filepath = filePath - bl_scene.render.image_settings.file_format = 'OPEN_EXR' + dspy_info = display_utils.get_beauty_filepath(bl_scene, use_blender_frame=True, expand_tokens=True, no_ext=True) + if display_utils.using_rman_displays(): + if write_comp: + bl_scene.render.filepath = dspy_info['filePath'] + img_format = display_utils.__RMAN_TO_BLENDER__.get(dspy_info['display_driver'], 'OPEN_EXR') + bl_scene.render.image_settings.file_format = img_format + else: + __BL_TMP_FILE__ = os.path.join(__BL_TMP_DIR__, '####.png') + bl_scene.render.filepath = __BL_TMP_FILE__ + bl_scene.render.image_settings.file_format = 'PNG' else: - __BL_TMP_FILE__ = os.path.join(__BL_TMP_DIR__, '####.png') - bl_scene.render.filepath = __BL_TMP_FILE__ - bl_scene.render.image_settings.file_format = 'PNG' + bl_scene.render.filepath = dspy_info['filePath'] @persistent def render_post(bl_scene): @@ -86,6 +93,9 @@ def render_post(bl_scene): global __ORIGINAL_BL_FILE_FORMAT__ global __BL_TMP_FILE__ + if bl_scene.render.engine != 'PRMAN_RENDER': + return + bl_scene.render.filepath = __ORIGINAL_BL_FILEPATH__ bl_scene.render.image_settings.file_format = __ORIGINAL_BL_FILE_FORMAT__ if __BL_TMP_FILE__: diff --git a/rman_render.py b/rman_render.py index b29c384a..488e1ec9 100644 --- a/rman_render.py +++ b/rman_render.py @@ -45,6 +45,37 @@ __DRAW_THREAD__ = None __RMAN_STATS_THREAD__ = None +# map Blender display file format +# to ice format +__BLENDER_TO_ICE_DSPY__ = { + 'TIFF': ice.constants.FMT_TIFFFLOAT, + 'TARGA': ice.constants.FMT_TGA, + 'TARGA_RAW': ice.constants.FMT_TGA, + 'JPEG': ice.constants.FMT_JPEG, + 'JPEG2000': ice.constants.FMT_JPEG, + 'OPEN_EXR': ice.constants.FMT_EXRFLOAT, + 'CINEON': ice.constants.FMT_CINEON, + 'PNG': ice.constants.FMT_PNG +} + +# map ice format to a file extension +__ICE_EXT_MAP__ = { + ice.constants.FMT_TIFFFLOAT: 'tif', + ice.constants.FMT_TGA: 'tga', + ice.constants.FMT_JPEG: 'jpg', + ice.constants.FMT_EXRFLOAT: 'exr', + ice.constants.FMT_CINEON: 'cin', + ice.constants.FMT_PNG: 'png' +} + +# map rman display to ice format +__RMAN_TO_ICE_DSPY__ = { + 'tiff': ice.constants.FMT_TIFFFLOAT, + 'targa': ice.constants.FMT_TGA, + 'openexr': ice.constants.FMT_EXRFLOAT, + 'png': ice.constants.FMT_PNG +} + def __update_areas__(): for window in bpy.context.window_manager.windows: for area in window.screen.areas: @@ -251,8 +282,9 @@ def preload_dsos(rman_render): class BlRenderResultHelper: - def __init__(self, rman_render, dspy_dict): + def __init__(self, rman_render, bl_scene, dspy_dict): self.rman_render = rman_render + self.bl_scene = bl_scene self.dspy_dict = dspy_dict self.width = -1 self.height = -1 @@ -342,6 +374,8 @@ def finish_passes(self): filepath = self.dspy_dict['displays'][dspy_nm]['filePath'] if i == 0: + continue + # write out the beauty with a 'raw' substring toks = os.path.splitext(filepath) filepath = '%s_beauty_raw.exr' % (toks[0]) @@ -354,25 +388,15 @@ def finish_passes(self): # use ice to save out the image img = ice.FromArray(buffer) img = img.Flip(False, True, False) - img.Save(filepath, ice.constants.FMT_EXRFLOAT) - else: - buffer = self.rman_render._get_buffer(self.width, self.height, image_num=i, as_flat=True) - if buffer is None: - continue - bl_image = bpy.data.images.new(dspy_nm, self.width, self.height) - try: - if isinstance(buffer, numpy.ndarray): - buffer = buffer.tolist() - bl_image.use_generated_float = True - bl_image.filepath_raw = filepath - bl_image.pixels.foreach_set(buffer) - bl_image.file_format = 'OPEN_EXR' - bl_image.update() - bl_image.save() - except: - pass - finally: - bpy.data.images.remove(bl_image) + img_format = ice.constants.FMT_EXRFLOAT + if not display_utils.using_rman_displays(): + img_format = __BLENDER_TO_ICE_DSPY__.get(self.bl_scene.render.image_settings.file_format, img_format) + + # change file extension + toks = os.path.splitext(filepath) + ext = __ICE_EXT_MAP__.get(img_format) + filepath = '%s.%s' % (toks[0], ext) + img.Save(filepath, img_format) class RmanRender(object): ''' @@ -693,7 +717,7 @@ def start_render(self, depsgraph, for_background=False): self.sg_scene.Render(render_cmd) if self.rman_render_into == 'blender': dspy_dict = display_utils.get_dspy_dict(self.rman_scene, include_holdouts=False) - bl_rr_helper = BlRenderResultHelper(self, dspy_dict) + bl_rr_helper = BlRenderResultHelper(self, self.bl_scene, dspy_dict) if for_background: bl_rr_helper.write_aovs = (use_compositor and rm.use_bl_compositor_write_aovs) else: From 30578eab14c5d100401e0fc9078a346b1904ea49 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 14 Dec 2022 07:42:29 -0800 Subject: [PATCH 221/278] Fixes to preset browser (CUSP-7251) * when setting the hostVersion for asset compatibility, use bpy.app.version_string * when creating the Maya shading node, make sure the node name does not contain dots. * remove an unneeded call to os.path.relpath. --- rman_presets/core.py | 7 ++++--- rman_presets/operators.py | 6 ++---- rman_presets/rmanAssetsBlender.py | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/rman_presets/core.py b/rman_presets/core.py index 66620c92..6db9e656 100644 --- a/rman_presets/core.py +++ b/rman_presets/core.py @@ -573,7 +573,8 @@ def export_material_preset(mat, nodes_to_convert, renderman_output_node, Asset): nodeClass = 'root' rmanNode = 'shadingEngine' nodeType = 'shadingEngine' - nodeName = '%s_SG' % Asset.label() + sg_name = Asset.label().replace('.', '_') + nodeName = '%s_SG' % sg_name Asset.addNode(nodeName, nodeType, nodeClass, rmanNode, externalosl=False) @@ -1465,7 +1466,7 @@ def export_asset(nodes, atype, infodict, category, cfg, renderPreview='std', # prmanversion = envconfig().build_info.version() Asset.setCompatibility(hostName='Blender', - hostVersion=bpy.app.version, + hostVersion=bpy.app.version_string, rendererVersion=prmanversion) # parse scene @@ -1508,4 +1509,4 @@ def export_asset(nodes, atype, infodict, category, cfg, renderPreview='std', # # print("exportAsset: %s..." % dirPath) Asset.save(jsonfile, compact=False) - return True \ No newline at end of file + return True diff --git a/rman_presets/operators.py b/rman_presets/operators.py index a4d0febe..5b3af2b5 100644 --- a/rman_presets/operators.py +++ b/rman_presets/operators.py @@ -678,16 +678,14 @@ def poll(cls, context): current_category_path = hostPrefs.getSelectedCategory() if current_category_path == '': return False - rel_path = os.path.relpath(current_category_path, hostPrefs.getSelectedLibrary()) - if rel_path in ['EnvironmentMaps', 'Materials', 'LightRigs']: + if current_category_path in ['EnvironmentMaps', 'Materials', 'LightRigs']: return False return True def execute(self, context): hostPrefs = rab.get_host_prefs() current_category_path = hostPrefs.getSelectedCategory() - rel_path = os.path.relpath(current_category_path, hostPrefs.getSelectedLibrary()) - ral.deleteCategory(hostPrefs.cfg, rel_path) + ral.deleteCategory(hostPrefs.cfg, current_category_path) self.op = getattr(context, 'op_ptr', None) if self.op: self.op.dummy_index = -1 diff --git a/rman_presets/rmanAssetsBlender.py b/rman_presets/rmanAssetsBlender.py index df875b71..b8235499 100644 --- a/rman_presets/rmanAssetsBlender.py +++ b/rman_presets/rmanAssetsBlender.py @@ -194,7 +194,7 @@ def bl_export_asset(nodes, atype, infodict, category, cfg, renderPreview='std', # prmanversion = envconfig().build_info.version() Asset.setCompatibility(hostName='Blender', - hostVersion=bpy.app.version, + hostVersion=bpy.app.version_string, rendererVersion=prmanversion) # parse maya scene @@ -232,4 +232,4 @@ def bl_export_asset(nodes, atype, infodict, category, cfg, renderPreview='std', Asset.save(jsonfile, compact=False) ral.renderAssetPreview(Asset, progress=None, rmantree=envconfig().rmantree) - return True \ No newline at end of file + return True From d6e4a6e1406937057d4dab9c2984b6619d7efd7c Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 17 Jan 2023 01:52:37 -0800 Subject: [PATCH 222/278] Don't try to replace strings with the token, if blend_dir is an empty string. --- rfb_utils/filepath_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rfb_utils/filepath_utils.py b/rfb_utils/filepath_utils.py index 956da9e0..b6ccf0a9 100644 --- a/rfb_utils/filepath_utils.py +++ b/rfb_utils/filepath_utils.py @@ -76,7 +76,9 @@ def get_token_blender_file_path(p): pout = re.sub(regex, '/', p, 0, re.MULTILINE) else: blend_dir = string_utils.get_var('blend_dir') - if blend_dir.endswith('/'): + if blend_dir == '': + pout = p + elif blend_dir.endswith('/'): pout = p.replace(blend_dir, '') else: pout = p.replace(blend_dir, '/') From c62aec844e536410ff535476a1b8c53ed762311e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 17 Jan 2023 04:29:58 -0800 Subject: [PATCH 223/278] Add a create_displacement function to the rfb_api module --- rfb_api/__init__.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/rfb_api/__init__.py b/rfb_api/__init__.py index 66f65747..a12f4689 100644 --- a/rfb_api/__init__.py +++ b/rfb_api/__init__.py @@ -144,7 +144,6 @@ def create_bxdf(node_type): Arguments: node_type (str) - name of the Bxdf you want to create (ex: PxrSurface) - ob (bpy.types.Object) - optional object to attach material to Returns: (bpy.types.Material, bpy.types.Node) - material and bxdf node @@ -156,6 +155,26 @@ def create_bxdf(node_type): bxdf = output.inputs[0].links[0].from_node return (material, bxdf) +def create_displacement(material): + '''Create a PxrDisplace node for the given material + + Arguments: + node_type (bpy.types.Material) - material + + Returns: + (bpy.types.Node) - the PxrDisplace node + + ''' + nt = material.node_tree + output = shadergraph_utils.find_node_from_nodetree(nt, 'RendermanOutputNode') + if output is None: + return None + if output.inputs['displace_in'].is_linked: + return output.inputs['displace_in'].links[0].from_node + disp = nt.nodes.new('PxrDisplaceDisplaceNode') + nt.links.new(disp.outputs['displace_out'], output.inputs['displace_in']) + return disp + def attach_material(material, ob): '''Attach material to object From 1d6bded0f40c085b8cec0bcff0cb099d57eba6e7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 17 Jan 2023 04:46:11 -0800 Subject: [PATCH 224/278] Make sure we always write the displacement bounds attributes. --- rman_config/config/rman_properties_object.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index 13fe11c6..ad5c68a7 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -688,6 +688,7 @@ "label": "Displacement Bound", "type": "float", "editable": true, + "always_write": true, "default": 0.1, "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_displacementBound', c)", "page": "Displacement" @@ -700,6 +701,7 @@ "type": "string", "default": "object", "editable": true, + "always_write": true, "widget": "popup", "options": "world|object", "page": "Displacement", From 8c5cb83446654f9c59435f5d223e5b35899750c7 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 19 Jan 2023 02:10:37 -0800 Subject: [PATCH 225/278] Add new required stylized AOV (NPRdistort) --- rman_config/config/rman_dspychan_definitions.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rman_config/config/rman_dspychan_definitions.json b/rman_config/config/rman_dspychan_definitions.json index b00e3d6e..f7dfd67d 100644 --- a/rman_config/config/rman_dspychan_definitions.json +++ b/rman_config/config/rman_dspychan_definitions.json @@ -723,7 +723,12 @@ "channelType": "color", "channelSource": "NPRtoonDiffRamp", "group": "NPR" - } + }, + "NPRdistort": { + "channelType": "color", + "channelSource": "NPRdistort", + "group": "NPR" + } }, "displays": { "Default": { @@ -802,7 +807,8 @@ "NPRalbedo", "NPRtextureCoords", "NPRPtriplanar", - "NPRNtriplanar" + "NPRNtriplanar", + "NPRdistort" ] } } From a39e70ebc09dde9bb0da6b429fb75b0861f7b570 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 24 Jan 2023 03:25:58 -0800 Subject: [PATCH 226/278] Fix a couple places where there were circular imports in rfb_utils. --- rfb_utils/display_utils.py | 5 +++-- rfb_utils/property_utils.py | 22 ++++++++++++++-------- rfb_utils/scene_utils.py | 3 ++- rfb_utils/shadergraph_utils.py | 3 ++- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 6cd5f2da..7cc7211c 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -1,7 +1,5 @@ from . import string_utils from . import property_utils -from . import shadergraph_utils -from . import scene_utils from .. import rman_constants from .. import rman_config from collections import OrderedDict @@ -692,6 +690,8 @@ def make_dspy_info(scene, is_interactive=False): (str) - a string with the display notes to give to "it" """ + from . import shadergraph_utils + params = {} rm = scene.renderman world = scene.world @@ -742,6 +742,7 @@ def export_metadata(scene, params, camera_name=None): params (RtParamList) - param list to fill with meta data camera_name - Name of camera we want meta data from """ + from . import shadergraph_utils rm = scene.renderman world = scene.world diff --git a/rfb_utils/property_utils.py b/rfb_utils/property_utils.py index e289fa38..530a1f8d 100644 --- a/rfb_utils/property_utils.py +++ b/rfb_utils/property_utils.py @@ -1,16 +1,9 @@ -from operator import methodcaller -from . import texture_utils from . import string_utils -from . import shadergraph_utils from . import prefs_utils -from ..rman_constants import RFB_ARRAYS_MAX_LEN, __RMAN_EMPTY_STRING__, __RESERVED_BLENDER_NAMES__, RFB_FLOAT3 +from ..rman_constants import __RMAN_EMPTY_STRING__, __RESERVED_BLENDER_NAMES__, RFB_FLOAT3 from ..rfb_logger import rfb_log -from collections import OrderedDict from bpy.props import * import bpy -import os -import shutil -import re __GAINS_TO_ENABLE__ = { @@ -55,6 +48,9 @@ class BlPropInfo: def __init__(self, node, prop_name, prop_meta): + + from . import shadergraph_utils + self.prop_meta = prop_meta self.prop_name = prop_name self.prop = getattr(node, prop_name, None) @@ -322,6 +318,9 @@ def set_riattr_bl_prop(attrs, prop_name, meta, rm, check_inherit=True, remove=Tr def build_output_param_str(rman_sg_node, mat_name, from_node, from_socket, convert_socket=False, param_type=''): + + from . import shadergraph_utils + nodes_to_blnodeinfo = getattr(rman_sg_node, 'nodes_to_blnodeinfo', dict()) if from_node in nodes_to_blnodeinfo: bl_node_info = nodes_to_blnodeinfo[from_node] @@ -344,6 +343,9 @@ def build_output_param_str(rman_sg_node, mat_name, from_node, from_socket, conve return "%s:%s" % (from_node_name, from_sock_name) def get_output_param_str(rman_sg_node, node, mat_name, socket, to_socket=None, param_type='', check_do_convert=True): + + from . import shadergraph_utils + # if this is a node group, hook it up to the input node inside! if node.bl_idname == 'ShaderNodeGroup': ng = node.node_tree @@ -843,6 +845,8 @@ def set_node_rixparams(node, rman_sg_node, params, ob=None, mat_name=None, group val = [0, 0, 0] if param_type == 'color' else 0 elif param_type == 'string': + from . import texture_utils + if not is_frame_sensitive: is_frame_sensitive = string_utils.check_frame_sensitive(prop) @@ -882,6 +886,8 @@ def portal_inherit_dome_params(portal_node, dome, dome_node, rixparams): it is parented to. ''' + from . import texture_utils + inheritAttrs = { "float specular": 1.0, "float diffuse": 1.0, diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index b80b0983..a5505d5a 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -2,7 +2,6 @@ from . import object_utils from . import prefs_utils from . import string_utils -from . import display_utils from ..rfb_logger import rfb_log import bpy import sys @@ -57,6 +56,8 @@ def should_use_bl_compositor(bl_scene): Returns: (bool) - true if we should use the compositor; false if not ''' + from . import display_utils + rm = bl_scene.renderman if not bpy.app.background: return (rm.render_into == 'blender') diff --git a/rfb_utils/shadergraph_utils.py b/rfb_utils/shadergraph_utils.py index cc104f4e..bb47ee66 100644 --- a/rfb_utils/shadergraph_utils.py +++ b/rfb_utils/shadergraph_utils.py @@ -2,7 +2,6 @@ from . import filepath_utils from . import string_utils from . import object_utils -from . import scene_utils from ..rman_constants import RMAN_STYLIZED_FILTERS, RMAN_STYLIZED_PATTERNS, RMAN_UTILITY_PATTERN_NAMES, RFB_FLOAT3 import math import bpy @@ -477,6 +476,8 @@ def get_all_shading_nodes(scene=None): (list) - list of all the shading nodes ''' + from . import scene_utils + nodes = list() if not scene: From d9a56dba37c93ea89bfd9c45a40b9bc3ef30fde1 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 30 Jan 2023 07:57:58 -0800 Subject: [PATCH 227/278] Rename our "editable" meta keyword for our config files to "ipr_editable". "editable" is already a keyword in the Args file format, and it means something else. --- rfb_utils/draw_utils.py | 4 +- .../rfb_node_desc_param.py | 2 +- .../config/rman_properties_camera.json | 40 ++--- rman_config/config/rman_properties_curve.json | 20 +-- rman_config/config/rman_properties_mesh.json | 20 +-- .../config/rman_properties_object.json | 138 +++++++++--------- rman_config/config/rman_properties_scene.json | 16 +- .../config/rman_properties_volume.json | 8 +- 8 files changed, 124 insertions(+), 124 deletions(-) diff --git a/rfb_utils/draw_utils.py b/rfb_utils/draw_utils.py index 1e140920..e662ad78 100644 --- a/rfb_utils/draw_utils.py +++ b/rfb_utils/draw_utils.py @@ -174,7 +174,7 @@ def _draw_ui_from_rman_config(config_name, panel, context, layout, parent): page_prop = '' page_open = False page_name = '' - editable = getattr(ndp, 'editable', False) + ipr_editable = getattr(ndp, 'ipr_editable', False) is_enabled = True if hasattr(ndp, 'page') and ndp.page != '': page_prop = ndp.page + "_uio" @@ -268,7 +268,7 @@ def _draw_ui_from_rman_config(config_name, panel, context, layout, parent): row.prop(parent, ndp.name, text=label) if is_rman_interactive_running: - row.enabled = editable + row.enabled = ipr_editable elif is_rman_running: row.enabled = False else: diff --git a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py index 163544a8..81e8d52c 100644 --- a/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py +++ b/rfb_utils/rfb_node_desc_utils/rfb_node_desc_param.py @@ -13,7 +13,7 @@ 'inherit_true_value', 'update_function_name', 'update_function', 'set_function_name', 'set_function', 'get_function_name', 'get_function', - 'readOnly', 'always_write'] + 'readOnly', 'always_write', 'ipr_editable'] def blender_finalize(obj): """Post-process some parameters for Blender. diff --git a/rman_config/config/rman_properties_camera.json b/rman_config/config/rman_properties_camera.json index fc44c16b..08f76d2c 100644 --- a/rman_config/config/rman_properties_camera.json +++ b/rman_config/config/rman_properties_camera.json @@ -6,7 +6,7 @@ "name": "rman_use_dof", "label": "Depth of Field", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkbox", "page": "", @@ -16,7 +16,7 @@ "panel": "DATA_PT_renderman_camera", "name": "rman_aperture_fstop", "label": "F Stop", - "editable": true, + "ipr_editable": true, "type": "float", "default": 2.8, "page": "Aperture Controls", @@ -32,7 +32,7 @@ "name": "rman_aperture_ratio", "label": "Ratio", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "Aperture Controls", "help": "" @@ -42,7 +42,7 @@ "name": "rman_aperture_blades", "label": "Blades", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "page": "Aperture Controls", "help": "" @@ -52,7 +52,7 @@ "name": "rman_aperture_rotation", "label": "Rotation", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "min": -180.0, "max": 180.0, @@ -64,7 +64,7 @@ "name": "rman_aperture_roundness", "label": "Roundness", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "min": 0.0, "max": 1.0, @@ -76,7 +76,7 @@ "name": "rman_aperture_density", "label": "Density", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "min": 0.0, "max": 1.0, @@ -89,7 +89,7 @@ "name": "rman_focus_object", "label": "Focus Object", "type": "string", - "editable": true, + "ipr_editable": true, "widget": "bl_scenegraphLocation", "options": "nodeType:bpy.types.Object", "default": "" @@ -100,7 +100,7 @@ "name": "rman_focus_distance", "label": "Distance", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "conditionalVisOps": { "conditionalVisOp": "equalTo", @@ -115,7 +115,7 @@ "name": "rman_use_shutteropening", "label": "Control Shutter Opening", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkbox", "page": "Shutter", @@ -126,7 +126,7 @@ "name": "rman_shutteropening_c1", "label": "C1", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "Shutter", "help": "", @@ -141,7 +141,7 @@ "name": "rman_shutteropening_c2", "label": "C2", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "Shutter", "help": "", @@ -156,7 +156,7 @@ "name": "rman_shutteropening_d1", "label": "D1", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "Shutter", "help": "", @@ -171,7 +171,7 @@ "name": "rman_shutteropening_d2", "label": "D2", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "Shutter", "help": "", @@ -186,7 +186,7 @@ "name": "rman_shutteropening_e1", "label": "E1", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "Shutter", "help": "", @@ -201,7 +201,7 @@ "name": "rman_shutteropening_e2", "label": "E2", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "Shutter", "help": "", @@ -216,7 +216,7 @@ "name": "rman_shutteropening_f1", "label": "F1", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "Shutter", "help": "", @@ -231,7 +231,7 @@ "name": "rman_shutteropening_f2", "label": "F2", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "Shutter", "help": "", @@ -246,7 +246,7 @@ "name": "rman_stereoplanedepths", "label": "Stereo Plane Depths", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "size": -1, "page": "Stereo Planes", @@ -257,7 +257,7 @@ "name": "rman_stereoplaneoffsets", "label": "Stereo Plane Offsets", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "size": -1, "page": "Stereo Planes", diff --git a/rman_config/config/rman_properties_curve.json b/rman_config/config/rman_properties_curve.json index a739a3c8..851eed14 100644 --- a/rman_config/config/rman_properties_curve.json +++ b/rman_config/config/rman_properties_curve.json @@ -7,7 +7,7 @@ "primvar": "polygon:smoothnormals", "label": "Smooth Normals", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkBox", "page": "Polygons", @@ -19,7 +19,7 @@ "primvar": "polygon:smoothdisplacement", "label": "Prevent polygon cracking", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkBox", "page": "Polygons", @@ -30,7 +30,7 @@ "name": "rman_subdiv_scheme", "label": "Scheme", "type": "string", - "editable": true, + "ipr_editable": true, "default": "none", "widget": "mapper", "options": "None:none|Catmull-Clark:catmull-clark|Loop:loop|Bilinear:bilinear", @@ -42,7 +42,7 @@ "primvar": "dice:watertight", "label": "Water-tight dicing", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "mapper", "options": "Yes:1|No:0", @@ -59,7 +59,7 @@ "name": "rman_subdivInterp", "label": "Interpolation", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "mapper", "options": "No interpolation:0|Sharp creases and corners:1|Sharp creases:2", @@ -75,7 +75,7 @@ "name": "rman_subdivFacevaryingInterp", "label": "Face-Varying Interpolation", "type": "int", - "editable": true, + "ipr_editable": true, "default": 3, "widget": "mapper", "options": "Old Style:0|New Style:1|New Style, no corners:2|New Style, internal only:3", @@ -91,7 +91,7 @@ "name": "export_default_uv", "label": "Export Default UV", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "help": "Export the active UV set as the default 'st' primitive variable", @@ -102,7 +102,7 @@ "name": "export_default_tangents", "label": "Export Default Tangents", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkbox", "help": "Export the tangent and bitangent vectors for the active UV set. They will be exported as 'Tn' and 'Bn' primitive variables, respectively", @@ -118,7 +118,7 @@ "name": "export_default_vcol", "label": "Export Default Vertex Color", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "help": "Export the active Vertex Color set as the default 'Cs' primitive variable", @@ -130,7 +130,7 @@ "primvar": "curve:widthaffectscurvature", "label": "Width Affects Curvature", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "help": "When true the curve width of round curves is taken into account in the computation of the tube curvature, otherwise only the curvature along the curve is. This control does not affect curve ribbons.", diff --git a/rman_config/config/rman_properties_mesh.json b/rman_config/config/rman_properties_mesh.json index 1c28ba0b..a1fcd8c6 100644 --- a/rman_config/config/rman_properties_mesh.json +++ b/rman_config/config/rman_properties_mesh.json @@ -7,7 +7,7 @@ "primvar": "polygon:smoothnormals", "label": "Smooth Normals", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkBox", "page": "Polygons", @@ -20,7 +20,7 @@ "primvar": "polygon:smoothdisplacement", "label": "Prevent polygon cracking", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkBox", "page": "Polygons", @@ -32,7 +32,7 @@ "name": "rman_subdiv_scheme", "label": "Scheme", "type": "string", - "editable": true, + "ipr_editable": true, "default": "none", "widget": "mapper", "options": "None:none|Catmull-Clark:catmull-clark|Loop:loop|Bilinear:bilinear", @@ -44,7 +44,7 @@ "primvar": "dice:watertight", "label": "Water-tight dicing", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "mapper", "options": "Yes:1|No:0", @@ -62,7 +62,7 @@ "name": "rman_subdivInterp", "label": "Interpolation", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "mapper", "options": "No interpolation:0|Sharp creases and corners:1|Sharp creases:2", @@ -78,7 +78,7 @@ "name": "rman_subdivFacevaryingInterp", "label": "Face-Varying Interpolation", "type": "int", - "editable": true, + "ipr_editable": true, "default": 3, "widget": "mapper", "options": "Old Style:0|New Style:1|New Style, no corners:2|New Style, internal only:3", @@ -96,7 +96,7 @@ "type": "string", "default": "", "page": "Subdivision Mesh", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "options": "prop_parent:context.object|prop_name:face_maps", "help": "You can select a face map to act as holes for your subdiv.", @@ -111,7 +111,7 @@ "name": "export_default_uv", "label": "Export Default UV", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "help": "Export the active UV set as the default 'st' primitive variable", @@ -122,7 +122,7 @@ "name": "export_default_tangents", "label": "Export Default Tangents", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "checkbox", "help": "Export the tangent and bitangent vectors for the active UV set. They will be exported as 'Tn' and 'Bn' primitive variables, respectively", @@ -138,7 +138,7 @@ "name": "export_default_vcol", "label": "Export Default Vertex Color", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "help": "Export the active Vertex Color set as the default 'Cs' primitive variable", diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index ad5c68a7..b5d04c84 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -94,7 +94,7 @@ "name": "quadric_cone_height", "label": "Height", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "help": "Height offset above XY plane", @@ -109,7 +109,7 @@ "name": "quadric_disk_height", "label": "Height", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "", "help": "Height offset above XY plane", @@ -124,7 +124,7 @@ "name": "quadric_radius", "label": "Radius", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "conditionalVisOps": { @@ -138,7 +138,7 @@ "name": "quadric_majorradius", "label": "Major Radius", "type": "float", - "editable": true, + "ipr_editable": true, "default": 2.0, "page": "", "conditionalVisOps": { @@ -152,7 +152,7 @@ "name": "quadric_majorradius", "label": "Major Radius", "type": "float", - "editable": true, + "ipr_editable": true, "default": 2.0, "page": "", "help": "Radius of Torus ring", @@ -167,7 +167,7 @@ "name": "quadric_minorradius", "label": "Minor Radius", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.5, "page": "", "help": "Radius of Torus cross-section circle", @@ -182,7 +182,7 @@ "name": "quadric_phimin", "label": "Minimum Cross-section", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "", "help": "Minimum angle of cross-section circle", @@ -197,7 +197,7 @@ "name": "quadric_phimax", "label": "Maximum Cross-section", "type": "float", - "editable": true, + "ipr_editable": true, "default": 360.0, "page": "", "help": "Maximum angle of cross-section circle", @@ -212,7 +212,7 @@ "name": "quadric_zmin", "label": "Z min", "type": "float", - "editable": true, + "ipr_editable": true, "default": -1.0, "page": "", "help": "Minimum height clipping of the primitive", @@ -227,7 +227,7 @@ "name": "quadric_zmax", "label": "Z max", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "help": "Maximum height clipping of the primitive", @@ -242,7 +242,7 @@ "name": "quadric_sweepangle", "label": "Sweep Angle", "type": "float", - "editable": true, + "ipr_editable": true, "default": 360.0, "page": "", "help": "Angle of clipping around the Z axis", @@ -258,7 +258,7 @@ "name": "runprogram_path", "label": "RunProgram Path", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "widget": "fileinput", @@ -269,7 +269,7 @@ "name": "runprogram_args", "label": "RunProgram Arguments", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "help": "Command line arguments to RunProgram" @@ -280,7 +280,7 @@ "name": "path_dso", "label": "DSO Path", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "widget": "fileinput", @@ -291,7 +291,7 @@ "name": "path_dso_initial_data", "label": "DSO Initial Data", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "help": "Parameters to send the DSO" @@ -302,7 +302,7 @@ "name": "path_archive", "label": "Archive Path", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "widget": "fileinput", @@ -323,7 +323,7 @@ "name": "abc_filepath", "label": "File Path", "type": "string", - "editable": true, + "ipr_editable": true, "default": "", "page": "", "widget": "fileinput", @@ -334,7 +334,7 @@ "name": "abc_use_scene_frame", "label": "Use Scene Frame", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "", "widget": "checkbox", @@ -345,7 +345,7 @@ "name": "abc_frame", "label": "Frame", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "help": "Frame", @@ -360,7 +360,7 @@ "name": "abc_fps", "label": "FPS", "type": "float", - "editable": true, + "ipr_editable": true, "default": 24.0, "page": "", "help": "Frames per second" @@ -370,7 +370,7 @@ "name": "abc_velocityScale", "label": "Velocity Scale", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "help": "Scale the velocity primvar used to motion blur primitives with time-varying topology. Restart the renderer to see edits." @@ -380,7 +380,7 @@ "name": "primitive_point_width", "label": "Point Width", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.1, "page": "", "help": "Point Width" @@ -395,7 +395,7 @@ "widget": "mapper", "options": "Inherit:-1|Yes:1|No:0", "default": -1, - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_matteObject', c)", @@ -411,7 +411,7 @@ "widget": "mapper", "options": "Inherit:-1|Yes:1|No:0", "default": -1, - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_holdout', c)", @@ -425,7 +425,7 @@ "label": "Index of Refraction", "type": "float", "default": -1, - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_indexofrefraction', c)", @@ -438,7 +438,7 @@ "riattr": "shade:minsamples", "label": "Min Samples", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_minsamples', c)", "page": "Shading", @@ -450,7 +450,7 @@ "riattr": "shade:relativepixelvariance", "label": "Relative Pixel Variance", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_relativepixelvariance', c)", "page": "Shading", @@ -465,7 +465,7 @@ "default": -1, "inheritable": true, "inherit_true_value": -1, - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_maxDiffuseDepth', c)", "page": "Trace", "help": "Maximum diffuse light bounces." @@ -476,7 +476,7 @@ "riattr": "trace:maxspeculardepth", "label": "Max Specular Depth", "type": "int", - "editable": true, + "ipr_editable": true, "default": -1, "inheritable": true, "inherit_true_value": -1, @@ -492,7 +492,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_reflectExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -506,7 +506,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_reflectSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -520,7 +520,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_shadowExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -534,7 +534,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_shadowSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -548,7 +548,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_transmitExcludeSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -562,7 +562,7 @@ "type": "string", "default": "", "page": "Trace", - "editable": true, + "ipr_editable": true, "widget": "propSearch", "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_transmitSubset', c)", "options": "prop_parent:context.scene.renderman|prop_name:object_groups", @@ -576,7 +576,7 @@ "type": "string", "default": "", "page": "", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_lighting_excludesubset_string', c)", "help": "Lighting exclude subset" }, @@ -588,7 +588,7 @@ "type": "string", "default": "", "page": "", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_riattr_func(s, 'rman_lightfilter_subset_string', c)", "help": "Light filter subset" }, @@ -598,7 +598,7 @@ "riattr": "trace:intersectpriority", "label": "Intersection Priority", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "min": 0, "max": 31, @@ -612,7 +612,7 @@ "primvar": "trace:displacements", "label": "Trace Displacement", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkBox", "page": "" @@ -625,7 +625,7 @@ "type": "int", "default": -1, "widget": "mapper", - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "options": "Inherit:-1|Yes:1|No:0", @@ -637,7 +637,7 @@ "primvar": "trace:bias", "label": "Trace Bias", "type": "float", - "editable": true, + "ipr_editable": true, "default": -1, "inheritable": true, "inherit_true_value": -1, @@ -656,7 +656,7 @@ "type": "int", "default": -1, "widget": "mapper", - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "page": "Bias", @@ -670,7 +670,7 @@ "label": "Trace Bias", "type": "float", "default": -1, - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1, "page": "Bias", @@ -687,7 +687,7 @@ "primvar": "displacementbound:sphere", "label": "Displacement Bound", "type": "float", - "editable": true, + "ipr_editable": true, "always_write": true, "default": 0.1, "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_displacementBound', c)", @@ -700,7 +700,7 @@ "label": "Coordinate System", "type": "string", "default": "object", - "editable": true, + "ipr_editable": true, "always_write": true, "widget": "popup", "options": "world|object", @@ -715,7 +715,7 @@ "label": "MicroPolygon Length", "type": "float", "default": -1.0, - "editable": true, + "ipr_editable": true, "inheritable": true, "inherit_true_value": -1.0, "page": "Dicing", @@ -740,7 +740,7 @@ "label": "MicroPolygon Length", "type": "float", "default": 10.0, - "editable": true, + "ipr_editable": true, "always_write": true, "page": "Dicing", "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_micropolygonlength_volume', c)", @@ -763,7 +763,7 @@ "name": "rman_diceStrategy", "primvar": "dice:strategy", "type": "string", - "editable": true, + "ipr_editable": true, "label": "Dicing Strategy", "default": "instanceprojection", "widget": "popup", @@ -778,7 +778,7 @@ "primvar": "dice:minlength", "label": "Min Length", "type": "float", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_minlength', c)", "default": 1 }, @@ -791,7 +791,7 @@ "type": "string", "default": "world", "widget": "popup", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_minlengthspace', c)", "options": "world|object|camera" }, @@ -802,7 +802,7 @@ "primvar": "dice:rasterorient", "label": "Raster-Oriented Dicing", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "page": "Dicing", @@ -821,7 +821,7 @@ "primvar": "dice:worlddistancelength", "update_function_name": "lambda s, c: update_primvar_func(s, 'rman_diceDistanceLength', c)", "type": "float", - "editable": true, + "ipr_editable": true, "label": "Dicing Distance Length", "default": -1.0, "min": -1, @@ -839,7 +839,7 @@ "name": "dice_referenceCameraType", "label": "Dice Camera", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "mapper", "options": "Inherit from Globals:0|Reference Camera:1", @@ -852,7 +852,7 @@ "label": "Reference Camera", "primvar": "dice:referencecamera", "type": "string", - "editable": true, + "ipr_editable": true, "widget": "bl_scenegraphLocation", "options": "nodeType:bpy.types.Camera", "update_function_name": "lambda s, c: update_primvar_func(s, 'dice_referenceCamera', c)", @@ -870,7 +870,7 @@ "label": "Temporal Method", "primvar": "volume:temporalmethod", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "widget": "mapper", "options": "Eulerian:0|Reves:1", @@ -895,7 +895,7 @@ "label": "Velocity frames per second", "primvar": "volume:fps", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1, "conditionalVisOps": { "conditionalVis1Path": "bl_object_type", @@ -918,7 +918,7 @@ "label": "Shutter Offset", "primvar": "volume:shutteroffset", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1, "conditionalVisOps": { "conditionalVis1Path": "bl_object_type", @@ -941,7 +941,7 @@ "label": "Velocity Shutter Correction", "primvar": "volume:velocityshuttercorrection", "type": "int", - "editable": true, + "ipr_editable": true, "default": 0, "conditionalVisOps": { "conditionalVis1Path": "bl_object_type", @@ -965,7 +965,7 @@ "type": "int", "default": -1, "widget": "mapper", - "editable": true, + "ipr_editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, "inherit_true_value": -1, @@ -981,7 +981,7 @@ "type": "int", "default": -1, "widget": "mapper", - "editable": true, + "ipr_editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, "inherit_true_value": -1, @@ -997,7 +997,7 @@ "type": "int", "default": -1, "widget": "mapper", - "editable": true, + "ipr_editable": true, "options": "Inherit:-1|Yes:1|No:0", "inheritable": true, "inherit_true_value": -1, @@ -1010,7 +1010,7 @@ "name": "user_MatteID0", "label": "Matte ID 0", "riattr": "user:MatteID0", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1025,7 +1025,7 @@ "name": "user_MatteID1", "label": "Matte ID 1", "riattr": "user:MatteID1", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1040,7 +1040,7 @@ "name": "user_MatteID2", "label": "Matte ID 2", "riattr": "user:MatteID2", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1055,7 +1055,7 @@ "name": "user_MatteID3", "label": "Matte ID 3", "riattr": "user:MatteID3", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1070,7 +1070,7 @@ "name": "user_MatteID4", "label": "Matte ID 4", "riattr": "user:MatteID4", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1085,7 +1085,7 @@ "name": "user_MatteID5", "label": "Matte ID 5", "riattr": "user:MatteID5", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1100,7 +1100,7 @@ "name": "user_MatteID6", "label": "Matte ID 6", "riattr": "user:MatteID6", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, @@ -1115,7 +1115,7 @@ "name": "user_MatteID7", "label": "Matte ID 7", "riattr": "user:MatteID7", - "editable": true, + "ipr_editable": true, "type": "color", "default": [ 0, diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index d86b536e..f90d85cf 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -154,7 +154,7 @@ "default": 0, "widget": "checkbox", "help": "Keep render data around for faster animation renders, at the cost of increased memory usage. May not work in all cases.", - "editable": false, + "ipr_editable": false, "update_function_name": "update_do_persistent_data", "update_function": "def update_do_persistent_data(self, context):\n scene = context.scene\n scene.render.use_persistent_data = self.do_persistent_data" }, @@ -171,7 +171,7 @@ "widget": false, "slidermin": 0, "slidermax": 4096, - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_options_func(s, 'ipr_hider_minSamples', c)", "help": "When set to zero this value is automatically computed as the square root of the max samples." }, @@ -193,7 +193,7 @@ "Production": 1024, "High Quality": 2048 }, - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_options_func(s, 'ipr_hider_maxSamples', c)", "help": "The maximum number of camera rays to be traced for each pixel. When adaptive sampling is enabled (ie. Pixel Variance is greater than zero), fewer rays may be traced in smoother regions of the image." }, @@ -214,7 +214,7 @@ "Denoising": 0.05, "High Quality": 0.01 }, - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_options_func(s, 'ipr_ri_pixelVariance', c)", "help": "This value is applied during IPR. Adaptive sampling is done when Pixel Variance is greater than zero. Reducing this value increases the likelihood that more rays will be traced while increasing its value allows undersampling." }, @@ -229,7 +229,7 @@ "min": 0, "max": 6, "widget": false, - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_options_func(s, 'hider_decidither', c)", "help": "This value is only applied during IPR. The value determines how much refinement (in a dither pattern) will be applied to the image during interactive rendering. 0 means full refinement up to a value of 6 which is the least refinement per iteration." }, @@ -245,7 +245,7 @@ "widget": "mapper", "options": "100%:1.0|50%:0.5|33%:0.33|25%:0.25|12.5%:0.125", "help": "This value is only applied during IPR. This scales down the resolution during viewport renders.", - "editable": true, + "ipr_editable": true, "update_function_name": "update_viewport_res_mult", "update_function": "def update_viewport_res_mult(self, context):\n from ..rman_render import RmanRender\n rr = RmanRender.get_rman_render()\n rr.rman_scene_sync.update_viewport_res_mult(context)", "conditionalVisOps": { @@ -593,7 +593,7 @@ "riattr": "trace:maxspeculardepth", "label": "Max Specular Depth", "type": "int", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_root_node_func(s, 'ri_maxSpecularDepth', c)", "default": 4, "min": 0, @@ -606,7 +606,7 @@ "riattr": "trace:maxdiffusedepth", "label": "Max Diffuse Depth", "type": "int", - "editable": true, + "ipr_editable": true, "update_function_name": "lambda s, c: update_root_node_func(s, 'ri_maxDiffuseDepth', c)", "default": 1, "min": 0, diff --git a/rman_config/config/rman_properties_volume.json b/rman_config/config/rman_properties_volume.json index 955c3bce..9fd12013 100644 --- a/rman_config/config/rman_properties_volume.json +++ b/rman_config/config/rman_properties_volume.json @@ -6,7 +6,7 @@ "name": "openvdb_filterwidth", "label": "Filter Width", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "", "help": "If set to 0, disables mipmapping. If greater than 0, values less than 1 bias towards finer levels of the mipmap and values larger than one bias towards coarser levels." @@ -16,7 +16,7 @@ "name": "openvdb_densitymult", "label": "Density Multiplier", "type": "float", - "editable": true, + "ipr_editable": true, "default": 1.0, "page": "", "help": "Scales the values in the density grid of the volume. This should be more efficient than scaling the density in the shader." @@ -26,7 +26,7 @@ "name": "openvdb_densityrolloff", "label": "Density Rolloff", "type": "float", - "editable": true, + "ipr_editable": true, "default": 0.0, "page": "", "help": "Values greater than 0 produce a logarithmic falloff in density values. Smaller rolloff values produce greater falloff." @@ -36,7 +36,7 @@ "name": "volume_dsominmax", "label": "DSO Min/Max", "type": "int", - "editable": true, + "ipr_editable": true, "default": 1, "widget": "checkbox", "primvar": "volume:dsominmax", From 769b3321c040e24d1a97374c5c962713173b715a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 7 Feb 2023 14:27:38 -0800 Subject: [PATCH 228/278] Fixes for the viewport drawing of gobo and cookie light filters. * add overrides so that the "refreshMap" parameter doesn't show * radius should be set to 0, so that we draw non-rounded rectangles * add back the drawing of the texture, when the draw textured lights option is turned on. --- .../rman_properties_PxrCookieLightFilter.json | 9 ++++++ .../rman_properties_PxrGoboLightFilter.json | 9 ++++++ rman_ui/rman_ui_light_handlers/__init__.py | 30 +++++++++++-------- .../barn_light_filter_draw_helper.py | 4 +-- 4 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 rman_config/config/overrides/rman_properties_PxrCookieLightFilter.json create mode 100644 rman_config/config/overrides/rman_properties_PxrGoboLightFilter.json diff --git a/rman_config/config/overrides/rman_properties_PxrCookieLightFilter.json b/rman_config/config/overrides/rman_properties_PxrCookieLightFilter.json new file mode 100644 index 00000000..37e82d6e --- /dev/null +++ b/rman_config/config/overrides/rman_properties_PxrCookieLightFilter.json @@ -0,0 +1,9 @@ +{ + "name": "PxrCookieLightFilter.args", + "params": [ + { + "name": "refreshMap", + "widget": "__REMOVE__" + } + ] +} \ No newline at end of file diff --git a/rman_config/config/overrides/rman_properties_PxrGoboLightFilter.json b/rman_config/config/overrides/rman_properties_PxrGoboLightFilter.json new file mode 100644 index 00000000..9b5009ee --- /dev/null +++ b/rman_config/config/overrides/rman_properties_PxrGoboLightFilter.json @@ -0,0 +1,9 @@ +{ + "name": "PxrGoboLightFilter.args", + "params": [ + { + "name": "refreshMap", + "widget": "__REMOVE__" + } + ] +} \ No newline at end of file diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index a777b794..ff48f9e6 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -844,18 +844,14 @@ def draw_rect_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') - tex = '' - col = None if light_shader_name == 'PxrRectLight': + m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') tex = light_shader.lightColorMap col = light_shader.lightColor - elif light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter']: - tex = light_shader.map - pts = ((0.5, -0.5, 0.0), (-0.5, -0.5, 0.0), (-0.5, 0.5, 0.0), (0.5, 0.5, 0.0)) - uvs = ((1, 1), (0, 1), (0, 0), (1, 0)) - draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) + pts = ((0.5, -0.5, 0.0), (-0.5, -0.5, 0.0), (-0.5, 0.5, 0.0), (0.5, 0.5, 0.0)) + uvs = ((1, 1), (0, 1), (0, 0), (1, 0)) + draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) def draw_sphere_light(ob): global _FRUSTUM_DRAW_HELPER_ @@ -1430,7 +1426,7 @@ def draw_ramp_light_filter(ob): else: pass -def draw_barn_light_filter(ob): +def draw_barn_light_filter(ob, light_shader, light_shader_name): global _BARN_LIGHT_DRAW_HELPER_ _SHADER_.bind() @@ -1438,9 +1434,12 @@ def draw_barn_light_filter(ob): m = Matrix(ob.matrix_world) m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') - set_selection_color(ob) + set_selection_color(ob) - _BARN_LIGHT_DRAW_HELPER_.update_input_params(ob) + radius = 1.0 + if light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter']: + radius = 0.0 + _BARN_LIGHT_DRAW_HELPER_.update_input_params(ob, radius) vtx_buffer = _BARN_LIGHT_DRAW_HELPER_.vtx_buffer() pts = [m @ Vector(pt) for pt in vtx_buffer ] @@ -1450,6 +1449,13 @@ def draw_barn_light_filter(ob): draw_line_shape(ob, _SHADER_, pts, indices) + if light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter']: + col = light_shader.fillColor + tex = light_shader.map + pts = ((0.5, -0.5, 0.0), (-0.5, -0.5, 0.0), (-0.5, 0.5, 0.0), (0.5, 0.5, 0.0)) + uvs = ((1, 1), (0, 1), (0, 0), (1, 0)) + draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) + def draw(): global _PRMAN_TEX_CACHE_ global _RMAN_TEXTURED_LIGHTS_ @@ -1518,7 +1524,7 @@ def draw(): draw_ramp_light_filter(ob) elif light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter', 'PxrBarnLightFilter']: # get all lights that the barn is attached to - draw_barn_light_filter(ob) + draw_barn_light_filter(ob, light_shader, light_shader_name) else: draw_sphere_light(ob) diff --git a/rman_ui/rman_ui_light_handlers/barn_light_filter_draw_helper.py b/rman_ui/rman_ui_light_handlers/barn_light_filter_draw_helper.py index 74a3bfe6..af6464cc 100644 --- a/rman_ui/rman_ui_light_handlers/barn_light_filter_draw_helper.py +++ b/rman_ui/rman_ui_light_handlers/barn_light_filter_draw_helper.py @@ -95,7 +95,7 @@ def __init__(self): self.invert = 0 self.depth = 10.0 - def update_input_params(self, obj): + def update_input_params(self, obj, radius): self.parents = get_parented_lights(obj) self.num_lights = len(self.parents) @@ -111,7 +111,7 @@ def update_input_params(self, obj): self.useLightDirection = getattr(rm, "useLightDirection", 0) self.width = getattr(rm, "width", 1.0) self.height = getattr(rm, "height", 1.0) - self.radius = getattr(rm, "radius", 1.0) + self.radius = getattr(rm, "radius", radius) self.edge = getattr(rm, "edge", 0.0) self.scaleWidth = getattr(rm, "scaleWidth", 1.0) self.scaleHeight = getattr(rm, "scaleHeight", 1.0) From 5b6501a8958ca1ed418fbd4dda9fad2948698896 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 7 Feb 2023 14:37:08 -0800 Subject: [PATCH 229/278] Remove some unnecessary code in the light draw handler code. --- rman_ui/rman_ui_light_handlers/__init__.py | 57 +++++++++++----------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index ff48f9e6..5132f423 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -3,7 +3,6 @@ import bgl import os from gpu_extras.batch import batch_for_shader -from ...rfb_utils import texture_utils from ...rfb_utils import string_utils from ...rfb_utils import prefs_utils from ...rfb_logger import rfb_log @@ -537,8 +536,8 @@ def _get_sun_direction(ob): rm = light.renderman.get_light_node() m = Matrix.Identity(4) - m = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') - m = m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') + m = m @ __MTX_X_90__ + m = m @ __MTX_Y_90__ month = float(rm.month) day = float(rm.day) @@ -803,7 +802,7 @@ def draw_rect_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ box = [m @ Vector(pt) for pt in s_rmanLightLogo['box']] box_indices = _get_indices(s_rmanLightLogo['box']) @@ -845,7 +844,7 @@ def draw_rect_light(ob): draw_line_shape(ob, _SHADER_, pts, indices) if light_shader_name == 'PxrRectLight': - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ tex = light_shader.lightColorMap col = light_shader.lightColor @@ -861,18 +860,18 @@ def draw_sphere_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ disk = [m @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = m @ __MTX_Y_90__ disk = [m2 @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = m @ __MTX_X_90__ disk = [m3 @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -908,7 +907,7 @@ def draw_sphere_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ Matrix.Scale(0.5, 4) @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') + m = ob_matrix @ Matrix.Scale(0.5, 4) @ __MTX_X_90__ idx_buffer = make_sphere_idx_buffer() if light_shader_name in ['PxrSphereLight']: col = light_shader.lightColor @@ -931,7 +930,7 @@ def draw_envday_light(ob): ob_matrix = m m = Matrix(ob_matrix) - m = m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') + m = m @ __MTX_X_90__ west_rr_shape = [m @ Vector(pt) for pt in s_envday['west_rr_shape']] west_rr_indices = _get_indices(s_envday['west_rr_shape']) @@ -1006,7 +1005,7 @@ def draw_disk_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ disk = [m @ Vector(pt) for pt in s_diskLight] disk_indices = _get_indices(s_diskLight) @@ -1047,7 +1046,7 @@ def draw_disk_light(ob): indices = _FRUSTUM_DRAW_HELPER_.idx_buffer(len(pts), 0, 0) draw_line_shape(ob, _SHADER_, pts, indices) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ col = light_shader.lightColor draw_solid(ob, s_diskLight, m, col=col) @@ -1058,7 +1057,7 @@ def draw_dist_light(ob): set_selection_color(ob) ob_matrix = Matrix(ob.matrix_world) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ arrow1 = [m @ Vector(pt) for pt in s_distantLight['arrow1']] arrow1_indices = _get_indices(s_distantLight['arrow1']) @@ -1097,12 +1096,12 @@ def draw_portal_light(ob): R_inside_indices = _get_indices(s_rmanLightLogo['R_inside']) draw_line_shape(ob, _SHADER_, R_inside, R_inside_indices) - m = ob_matrix @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = ob_matrix @ __MTX_Y_180__ arrow = [m @ Vector(pt) for pt in s_rmanLightLogo['arrow']] arrow_indices = _get_indices(s_rmanLightLogo['arrow']) draw_line_shape(ob, _SHADER_, arrow, arrow_indices) - m = ob_matrix @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') + m = ob_matrix @ __MTX_X_90__ m = m @ Matrix.Scale(0.5, 4) rays = [m @ Vector(pt) for pt in s_portalRays] rays_indices = _get_indices(s_portalRays) @@ -1118,7 +1117,7 @@ def draw_dome_light(ob): scale = max(sca) # take the max axis m = Matrix.Rotation(angle, 4, axis) m = m @ Matrix.Scale(100 * scale, 4) - m = m @ __MTX_X_90__ # Matrix.Rotation(math.radians(90.0), 4, 'X') + m = m @ __MTX_X_90__ sphere_pts = make_sphere() sphere = [m @ Vector(p) for p in sphere_pts] @@ -1204,13 +1203,13 @@ def draw_rounded_rectangles(ob, left, right, b = radius+bottomEdge draw_arc(a, b, 10, 3, right, -bottom, pts) - translate = m #Matrix.Translation( Vector([0,0, zOffset1])) @ m + translate = m shape_pts = [translate @ Vector(pt) for pt in pts] shape_pts_indices = _get_indices(shape_pts) draw_line_shape(ob, _SHADER_, shape_pts, shape_pts_indices) - translate = m #Matrix.Translation( Vector([0,0, zOffset2])) @ m + translate = m shape_pts = [translate @ Vector(pt) for pt in pts] shape_pts_indices = _get_indices(shape_pts) @@ -1235,7 +1234,7 @@ def draw_rod(ob, leftEdge, rightEdge, topEdge, bottomEdge, topEdge, bottomEdge, front, -back, m) - m = world_mat @ __MTX_X_90__ #Matrix.Rotation(math.radians(-90.0), 4, 'X') + m = world_mat @ __MTX_X_90__ # top and bottom @@ -1244,7 +1243,7 @@ def draw_rod(ob, leftEdge, rightEdge, topEdge, bottomEdge, leftEdge, rightEdge, backEdge, frontEdge, top, -bottom, m) - m = world_mat @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') + m = world_mat @ __MTX_Y_90__ # left and right @@ -1258,7 +1257,7 @@ def draw_rod_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ light = ob.data rm = light.renderman.get_light_node() @@ -1350,7 +1349,7 @@ def draw_ramp_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ # begin begin_m = m @ Matrix.Scale(begin, 4) @@ -1359,11 +1358,11 @@ def draw_ramp_light_filter(ob): disk_indices = _get_indices(s_diskLight) draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = begin_m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = begin_m @ __MTX_Y_90__ disk = [m2 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = begin_m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = begin_m @ __MTX_X_90__ disk = [m3 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -1373,11 +1372,11 @@ def draw_ramp_light_filter(ob): disk = [end_m @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m2 = end_m @ __MTX_Y_90__ #Matrix.Rotation(math.radians(90.0), 4, 'Y') + m2 = end_m @ __MTX_Y_90__ disk = [m2 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) - m3 = end_m @ __MTX_X_90__ #Matrix.Rotation(math.radians(90.0), 4, 'X') + m3 = end_m @ __MTX_X_90__ disk = [m3 @ Vector(pt) for pt in s_diskLight] draw_line_shape(ob, _SHADER_, disk, disk_indices) @@ -1385,7 +1384,7 @@ def draw_ramp_light_filter(ob): elif rampType == 1: m = Matrix(ob.matrix_world) - m = m @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ box = [m @ Vector(pt) for pt in s_rmanLightLogo['box']] n = mathutils.geometry.normal(box) @@ -1410,7 +1409,7 @@ def draw_ramp_light_filter(ob): set_selection_color(ob) m = Matrix(ob.matrix_world) - m = m @ __MTX_Y_180__ #Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ disk_indices = _get_indices(s_diskLight) if begin > 0.0: @@ -1432,7 +1431,7 @@ def draw_barn_light_filter(ob, light_shader, light_shader_name): _SHADER_.bind() m = Matrix(ob.matrix_world) - m = m @ __MTX_Y_180__ # Matrix.Rotation(math.radians(180.0), 4, 'Y') + m = m @ __MTX_Y_180__ set_selection_color(ob) From 536bb889806b7be805129914fa666447ff5cfbdf Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Feb 2023 10:37:37 -0800 Subject: [PATCH 230/278] Add a pref to parent light filters to the selected light, similar to what is available in RfM. --- preferences.py | 12 +++++-- rman_operators/rman_operators_view3d.py | 46 +++++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/preferences.py b/preferences.py index a7300608..22b0efa1 100644 --- a/preferences.py +++ b/preferences.py @@ -91,7 +91,8 @@ 'rman_roz_webSocketServer': False, 'rman_roz_webSocketServer_Port': 0, 'rman_roz_stats_print_level': '1', - 'rman_enhance_zoom_factor': 5 + 'rman_enhance_zoom_factor': 5, + 'rman_parent_lightfilter': False } class RendermanPreferencePath(bpy.types.PropertyGroup): @@ -532,7 +533,13 @@ def update_stats_config(self, context): default=5, min=2, max=10 - ) + ) + + rman_parent_lightfilter: BoolProperty( + name="Parent Filter to Light", + default=False, + description="If on, and a light is selected, attaching a light filter will parent the light filter to the selected light." + ) def draw_xpu_devices(self, context, layout): if self.rman_xpu_device == 'CPU': @@ -594,6 +601,7 @@ def draw(self, context): col.prop(self, 'rman_invert_light_linking') col.prop(self, 'rman_solo_collapse_nodes') col.prop(self, 'rman_use_blend_dir_token') + col.prop(self, 'rman_parent_lightfilter') col.prop(self, 'rman_editor') col.prop(self, 'rman_enhance_zoom_factor') diff --git a/rman_operators/rman_operators_view3d.py b/rman_operators/rman_operators_view3d.py index 10a9f9ed..fd4ace8f 100644 --- a/rman_operators/rman_operators_view3d.py +++ b/rman_operators/rman_operators_view3d.py @@ -6,6 +6,7 @@ from ..rfb_utils import shadergraph_utils from ..rfb_utils import object_utils from ..rfb_utils import string_utils +from ..rfb_utils import prefs_utils from ..rfb_logger import rfb_log from .. import rfb_icons from ..rman_constants import RFB_ADDON_VERSION_STRING @@ -240,9 +241,7 @@ def description(cls, context, properties): info = get_description('lightfilter', properties.rman_lightfilter_name) return info - def execute(self, context): - selected_objects = context.selected_objects - + def create_lightfilter(self, context): light_filter = bpy.data.lights.new(self.rman_lightfilter_name, 'AREA') light_filter_ob = bpy.data.objects.new(self.rman_lightfilter_name, light_filter) @@ -267,19 +266,38 @@ def execute(self, context): if context.view_layer.objects.active: context.view_layer.objects.active.select_set(False) light_filter_ob.select_set(True) - context.view_layer.objects.active = light_filter_ob + context.view_layer.objects.active = light_filter_ob + + return light_filter_ob + def execute(self, context): + selected_objects = context.selected_objects if self.properties.add_to_selected: - for ob in selected_objects: - rman_type = object_utils._detect_primitive_(ob) - if rman_type == 'LIGHT': - light_filter_item = ob.data.renderman.light_filters.add() - light_filter_item.linked_filter_ob = light_filter_ob - elif shadergraph_utils.is_mesh_light(ob): - mat = ob.active_material - if mat: - light_filter_item = mat.renderman_light.light_filters.add() - light_filter_item.linked_filter_ob = light_filter_ob + if not selected_objects: + light_filter_ob = self.create_lightfilter(context) + else: + light_filter_ob = None + do_parent = prefs_utils.get_pref('rman_parent_lightfilter') + if not do_parent: + light_filter_ob = self.create_lightfilter(context) + for ob in selected_objects: + rman_type = object_utils._detect_primitive_(ob) + if rman_type == 'LIGHT': + if do_parent: + light_filter_ob = self.create_lightfilter(context) + light_filter_ob.parent = ob + light_filter_item = ob.data.renderman.light_filters.add() + light_filter_item.linked_filter_ob = light_filter_ob + elif shadergraph_utils.is_mesh_light(ob): + mat = ob.active_material + if mat: + if do_parent: + light_filter_ob = self.create_lightfilter(context) + light_filter_ob.parent = ob + light_filter_item = mat.renderman_light.light_filters.add() + light_filter_item.linked_filter_ob = light_filter_ob + else: + light_filter_ob = self.create_lightfilter(context) return {"FINISHED"} From 1b8aadda77fe8d14d78d35b2abf145d8f0cbccee Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Feb 2023 11:24:09 -0800 Subject: [PATCH 231/278] When we draw the gobo and cookie textures, make sure they are scaled by both the width and height parameters. --- rman_ui/rman_ui_light_handlers/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index 5132f423..599c7b80 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -1451,7 +1451,9 @@ def draw_barn_light_filter(ob, light_shader, light_shader_name): if light_shader_name in ['PxrGoboLightFilter', 'PxrCookieLightFilter']: col = light_shader.fillColor tex = light_shader.map - pts = ((0.5, -0.5, 0.0), (-0.5, -0.5, 0.0), (-0.5, 0.5, 0.0), (0.5, 0.5, 0.0)) + w = light_shader.width + h = light_shader.height + pts = ((0.5*w, -0.5*h, 0.0), (-0.5*w, -0.5*h, 0.0), (-0.5*w, 0.5*h, 0.0), (0.5*w, 0.5*h, 0.0)) uvs = ((1, 1), (0, 1), (0, 0), (1, 0)) draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) From 8fdaad9d729ffd575d2cb2c02a96d6c11cef0b37 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Feb 2023 13:11:40 -0800 Subject: [PATCH 232/278] Drawing the cookie and gobo textures to respect the invertU and invertV params. --- rman_ui/rman_ui_light_handlers/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index 599c7b80..46b020e5 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -1453,8 +1453,10 @@ def draw_barn_light_filter(ob, light_shader, light_shader_name): tex = light_shader.map w = light_shader.width h = light_shader.height + invertU = int(getattr(light_shader, 'invertU', False)) + invertV = int(getattr(light_shader, 'invertV', False)) pts = ((0.5*w, -0.5*h, 0.0), (-0.5*w, -0.5*h, 0.0), (-0.5*w, 0.5*h, 0.0), (0.5*w, 0.5*h, 0.0)) - uvs = ((1, 1), (0, 1), (0, 0), (1, 0)) + uvs = ((invertU-0, 1-invertV), (1-invertU, 1-invertV), (1-invertU, invertV-0), (invertU-0, invertV-0)) draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) def draw(): From f5ab80da3e213715b6e9b9525842e42a750d84ee Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Feb 2023 14:20:24 -0800 Subject: [PATCH 233/278] Slight modifications on the invertU and invertV code for gobo and cookie textures. --- rman_ui/rman_ui_light_handlers/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_light_handlers/__init__.py b/rman_ui/rman_ui_light_handlers/__init__.py index 46b020e5..2a1e1943 100644 --- a/rman_ui/rman_ui_light_handlers/__init__.py +++ b/rman_ui/rman_ui_light_handlers/__init__.py @@ -1455,8 +1455,11 @@ def draw_barn_light_filter(ob, light_shader, light_shader_name): h = light_shader.height invertU = int(getattr(light_shader, 'invertU', False)) invertV = int(getattr(light_shader, 'invertV', False)) + u = 1.0 - invertU + v = 1.0 - invertV pts = ((0.5*w, -0.5*h, 0.0), (-0.5*w, -0.5*h, 0.0), (-0.5*w, 0.5*h, 0.0), (0.5*w, 0.5*h, 0.0)) - uvs = ((invertU-0, 1-invertV), (1-invertU, 1-invertV), (1-invertU, invertV-0), (invertU-0, invertV-0)) + #uvs = ((0, 1), (1,1), (1, 0), (0,0)) + uvs = ((1.0-u, v), (u,v), (u, 1.0-v), (1.0-u, 1.0-v)) draw_solid(ob, pts, m, uvs=uvs, tex=tex, col=col) def draw(): From dca8bdad6667b18ca2d186ae604a447e904ed8c0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 9 Feb 2023 10:48:32 -0800 Subject: [PATCH 234/278] Fro now, add username to the stats server id. --- rman_stats/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rman_stats/__init__.py b/rman_stats/__init__.py index 1ab8e60c..91c1994a 100644 --- a/rman_stats/__init__.py +++ b/rman_stats/__init__.py @@ -1,13 +1,12 @@ from ..rfb_logger import rfb_log -import re import os -import sys import json import bpy import rman import threading import time +import getpass from collections import OrderedDict import rman_utils.stats_config.core as stcore @@ -184,7 +183,7 @@ def init_stats_session(self): self.rman_stats_session_config.LoadConfigFile(rman_stats_config_path, 'stats.ini') # do this once at startup - self.web_socket_server_id = 'rfb_statsserver_'+str(os.getpid()) + self.web_socket_server_id = 'rfb_statsserver_' + getpass.getuser() + '_' + str(os.getpid()) self.rman_stats_session_config.SetServerId(self.web_socket_server_id) # initialize session config with prefs, then add session From f6a5d9a6daef84a5af5b2415e7d9b4e80f362cdd Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 22 Feb 2023 16:05:07 -0800 Subject: [PATCH 235/278] Add initial support for the AI denoiser. * add a checkbox so that the user can switch between the old and the AI denoiser * add the extra AOV's needed for the AI denoiser * for single frame, we run the denoiser on each frame separately. --- rfb_utils/display_utils.py | 11 +- .../config/rman_dspychan_definitions.json | 17 +++ rman_config/config/rman_properties_scene.json | 68 ++++++++-- rman_spool.py | 119 +++++++++++++++++- 4 files changed, 200 insertions(+), 15 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 7cc7211c..7390338c 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -145,7 +145,7 @@ def _add_stylized_channels(dspys_dict, dspy_drv, rman_scene, expandTokens): 'params': dspy_params, 'dspyDriverParams': None} -def _add_denoiser_channels(dspys_dict, dspy_params): +def _add_denoiser_channels(dspys_dict, dspy_params, rman_scene): """ Add the necessary dspy channels for denoiser. We assume the beauty display will be used as the variance file @@ -169,9 +169,10 @@ def _add_denoiser_channels(dspys_dict, dspy_params): dspys_dict['displays']['beauty']['params']['displayChannels'].append(chan) - filePath = dspys_dict['displays']['beauty']['filePath'] - f,ext = os.path.splitext(filePath) - dspys_dict['displays']['beauty']['filePath'] = f + '_variance' + ext + if not rman_scene.bl_scene.renderman.use_ai_denoiser: + filePath = dspys_dict['displays']['beauty']['filePath'] + f,ext = os.path.splitext(filePath) + dspys_dict['displays']['beauty']['filePath'] = f + '_variance' + ext dspys_dict['displays']['beauty']['is_variance'] = True def _set_blender_dspy_dict(layer, dspys_dict, dspy_drv, rman_scene, expandTokens, do_optix_denoise=False): @@ -472,7 +473,7 @@ def _set_rman_dspy_dict(rm_rl, dspys_dict, dspy_drv, rman_scene, expandTokens, d 'dspyDriverParams': param_list } if aov_denoise and display_driver == 'openexr' and not rman_scene.is_interactive: - _add_denoiser_channels(dspys_dict, dspy_params) + _add_denoiser_channels(dspys_dict, dspy_params, rman_scene) if aov.name == 'beauty' and rman_scene.is_interactive: diff --git a/rman_config/config/rman_dspychan_definitions.json b/rman_config/config/rman_dspychan_definitions.json index f7dfd67d..b870e2c0 100644 --- a/rman_config/config/rman_dspychan_definitions.json +++ b/rman_config/config/rman_dspychan_definitions.json @@ -183,6 +183,13 @@ "statistics": "variance", "group": "Denoiser" }, + "albedo_mse": { + "description": "", + "channelType": "color", + "channelSource": "lpe:nothruput;noinfinitecheck;noclamp;unoccluded;overwrite;C<.S'passthru'>*((U2L)|O)", + "statistics": "mse", + "group": "Denoiser" + }, "diffuse": { "description": "", "channelType": "color", @@ -235,6 +242,13 @@ "statistics": "variance", "group": "Denoiser" }, + "normal_mse": { + "description": "", + "channelType": "normal", + "channelSource": "lpe:nothruput;noinfinitecheck;noclamp;unoccluded;overwrite;CU6L", + "statistics": "mse", + "group": "Denoiser" + }, "forward": { "description": "", "channelType": "vector", @@ -744,8 +758,10 @@ "description": "Create a variance file appropriate for the denoiser.", "channels": [ "mse", + "sampleCount", "albedo", "albedo_var", + "albedo_mse", "diffuse", "diffuse_mse", "specular", @@ -754,6 +770,7 @@ "zfiltered_var", "normal", "normal_var", + "normal_mse", "forward", "backward" ] diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index f90d85cf..50b309dc 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1525,27 +1525,79 @@ }, { "panel": "RENDER_PT_renderman_spooling_export_options", - "page": "", + "page": "Denoising", + "name": "use_ai_denoiser", + "label": "Use AI Denoiser", + "type": "int", + "default": 0, + "widget": "checkbox", + "help": "Use the AI Denoiser to denoise passes." + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", + "name": "ai_denoiser_asymmetry", + "label": "Asymmetry", + "type": "float", + "default": 0.0, + "min": 0.0, + "max": 2.0, + "help": "Controls the asymmetry value, 0 is best quality, higher values encourage the denoiser to avoid overblurring, leading to weaker denoising.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_ai_denoiser", + "conditionalVisValue": "0" + } + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", + "name": "ai_denoiser_verbose", + "label": "Verbose", + "type": "int", + "default": 0, + "widget": "checkbox", + "help": "Enable versbose output.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_ai_denoiser", + "conditionalVisValue": "0" + } + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", "name": "denoise_cmd", "label": "Custom Denoise Commands", "type": "string", "default": "", - "help": "Inserts a string of custom commands arguments into the denoising process, if selected" + "help": "Inserts a string of custom commands arguments into the denoising process (hyperion), if selected", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_ai_denoiser", + "conditionalVisValue": "1" + } }, { "panel": "RENDER_PT_renderman_spooling_export_options", - "page": "", + "page": "Denoising", "name": "denoise_gpu", "label": "Use GPU for denoising", "type": "int", "default": 0, "widget": "checkbox", - "help": "The denoiser will attempt to use the GPU (if available)", + "help": "The denoiser (hyperion) will attempt to use the GPU (if available)", "conditionalVisOps": { - "conditionalVisOp": "notEqualTo", - "conditionalVisPath": "current_platform", - "conditionalVisValue": "macOS" - } + "conditionalVis1Path": "use_ai_denoiser", + "conditionalVis2Op": "notEqualTo", + "conditionalVis1Value": "0", + "conditionalVisOp": "and", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "current_platform", + "conditionalVis2Value": "macOS" + } } ] } \ No newline at end of file diff --git a/rman_spool.py b/rman_spool.py index 24d40c5e..70b329d6 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -220,6 +220,115 @@ def generate_rib_render_tasks(self, anim, parent_task, tasktitle, parent_task.addChild(renderframestask) + def generate_aidenoise_tasks(self, start, last, by): + tasktitle = "AI Denoiser Renders" + parent_task = author.Task() + parent_task.title = tasktitle + rm = self.bl_scene.renderman + + # any cross frame? + do_cross_frame = False + dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) + for dspy,params in dspys_dict['displays'].items(): + if not params['denoise']: + continue + if params['denoise_mode'] == 'crossframe': + do_cross_frame = True + break + + if start == last: + do_cross_frame = False + + if do_cross_frame: + # for crossframe, do it all in one task + cur_frame = self.rman_scene.bl_frame_current + task = author.Task() + task.title = 'AI Denoise Cross Frame' + command = author.Command(local=False, service="PixarRender") + + command.argv = ["aidenoise_ui"] + + command.argv.append('--crossframe') + command.argv.append('run') + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=1, + asFilePath=True) + path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + command.argv.append('-a') + command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) + command.argv.append('-o') + command.argv.append(path) + + for frame_num in range(start, last + 1, by): + self.rman_render.bl_frame_current = frame_num + + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=frame_num, + asFilePath=True) + + path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + + for dspy,params in dspys_dict['displays'].items(): + if not params['denoise']: + continue + + if dspy == 'beauty': + command.argv.append(variance_file) + else: + aov_file = string_utils.expand_string(params['filePath'], + frame=frame_num, + asFilePath=True) + command.argv.append(aov_file) + + task.addCommand(command) + parent_task.addChild(task) + + else: + # single frame + cur_frame = self.rman_scene.bl_frame_current + for frame_num in range(start, last + 1, by): + self.rman_render.bl_frame_current = frame_num + + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=frame_num, + asFilePath=True) + + task = author.Task() + task.title = 'AI Denoise Frame %d' % frame_num + command = author.Command(local=False, service="PixarRender") + + command.argv = ["aidenoiser_ui"] + + if rm.ai_denoiser_verbose: + command.argv.append('-v') + command.argv.append('run') + path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + command.argv.append('-a') + command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) + command.argv.append('-o') + command.argv.append(path) + + for dspy,params in dspys_dict['displays'].items(): + if not params['denoise']: + continue + + if params['denoise_mode'] != 'singleframe': + continue + + if dspy == 'beauty': + command.argv.append(variance_file) + else: + aov_file = string_utils.expand_string(params['filePath'], + frame=frame_num, + asFilePath=True) + command.argv.append(aov_file) + + task.addCommand(command) + parent_task.addChild(task) + + self.rman_render.bl_frame_current = cur_frame + return parent_task + def generate_denoise_tasks(self, start, last, by): tasktitle = "Denoiser Renders" @@ -387,7 +496,10 @@ def blender_batch_render(self, bl_filename): # Don't generate denoise tasks if we're baking # or using the Blender compositor if rm.hider_type == 'RAYTRACE' and (not rm.use_bl_compositor or not scene.use_nodes): - parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) + if rm.use_ai_denoiser: + parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) + else: + parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) job.addChild(parent_task) scene_filename = bpy.data.filepath @@ -467,7 +579,10 @@ def batch_render(self): # Don't denoise if we're baking if rm.hider_type == 'RAYTRACE': - parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) + if rm.use_ai_denoiser: + parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) + else: + parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) job.addChild(parent_task) bl_filename = bpy.data.filepath From 94af39b5dc9b24e19724bac9d23434daa1530139 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 22 Feb 2023 16:06:25 -0800 Subject: [PATCH 236/278] Forgot to bump the scene version string (should be 25.0.1 to reflect Lama parameter changes). --- rman_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_constants.py b/rman_constants.py index f3856fd4..c38ba03f 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -33,7 +33,7 @@ RFB_SCENE_VERSION_MAJOR = RMAN_SUPPORTED_VERSION_MAJOR RFB_SCENE_VERSION_MINOR = RMAN_SUPPORTED_VERSION_MINOR -RFB_SCENE_VERSION_PATCH = 0 +RFB_SCENE_VERSION_PATCH = 1 RFB_SCENE_VERSION_STRING = '%d.%d.%d' % (RFB_SCENE_VERSION_MAJOR, RFB_SCENE_VERSION_MINOR, RFB_SCENE_VERSION_PATCH) RFB_ADDON_DESCRIPTION = 'RenderMan %d.%d integration' % (RMAN_SUPPORTED_VERSION_MAJOR, RMAN_SUPPORTED_VERSION_MINOR) From 048b10cc526aa4f0e6225bf8416c91cf475fd05a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 22 Feb 2023 16:37:52 -0800 Subject: [PATCH 237/278] Add support for "digits" property in the JSON config files. This controls the precision displayed for float values. --- rfb_utils/generate_property_utils.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rfb_utils/generate_property_utils.py b/rfb_utils/generate_property_utils.py index 0713082c..8339a580 100644 --- a/rfb_utils/generate_property_utils.py +++ b/rfb_utils/generate_property_utils.py @@ -230,6 +230,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun prop_stepsize = 3 if hasattr(sp, 'sensitivity'): prop_stepsize = -int(math.log10(sp.sensitivity)) + prop_precision = getattr(sp, 'digits', 3) prop = None @@ -311,7 +312,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun elif param_type == 'float': if sp.is_array(): prop = FloatProperty(name=param_label, - default=0.0, precision=3, + default=0.0, precision=prop_precision, step=prop_stepsize, description=param_help, set=set_function, @@ -355,7 +356,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun param_max = sp.slidermax if hasattr(sp, 'slidermax') else param_max prop = FloatProperty(name=param_label, - default=param_default, precision=3, + default=param_default, precision=prop_precision, soft_min=param_min, soft_max=param_max, step=prop_stepsize, description=param_help, set=set_function, get=get_function, update=update_function) @@ -367,7 +368,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun param_max = sp.slidermax if hasattr(sp, 'slidermax') else param_max prop = FloatProperty(name=param_label, - default=param_default, precision=3, + default=param_default, precision=prop_precision, soft_min=param_min, soft_max=param_max, step=prop_stepsize, description=param_help, set=set_function, get=get_function, update=update_function) @@ -525,6 +526,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun prop = FloatVectorProperty(name=param_label, default=param_default, size=3, subtype="NONE", + precision=prop_precision, description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = param_type elif param_type == 'point': @@ -532,6 +534,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun param_default = '0 0 0' prop = FloatVectorProperty(name=param_label, default=param_default, size=3, + precision=prop_precision, subtype="XYZ", description=param_help, set=set_function, get=get_function, update=update_function) renderman_type = param_type @@ -614,6 +617,7 @@ def generate_property(node, sp, update_function=None, set_function=None, get_fun prop = FloatVectorProperty(name=param_label, default=param_default, size=2, step=prop_stepsize, + precision=prop_precision, description=param_help, set=set_function, update=update_function) renderman_type = 'float' prop_meta['arraySize'] = 2 From ed8332e473f2a4fa297f2a74cce366b2c22f3a3a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 24 Feb 2023 15:52:33 -0800 Subject: [PATCH 238/278] Changes to support the aidenoiser and old denoiser binary changes: aidenoiser -> denoise denoise -> denoise_legacy. Also, we now default to using the AI denoiser. --- rfb_utils/display_utils.py | 2 +- rman_config/config/rman_properties_scene.json | 22 +++++----- rman_spool.py | 42 +++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/rfb_utils/display_utils.py b/rfb_utils/display_utils.py index 7390338c..741aa615 100644 --- a/rfb_utils/display_utils.py +++ b/rfb_utils/display_utils.py @@ -169,7 +169,7 @@ def _add_denoiser_channels(dspys_dict, dspy_params, rman_scene): dspys_dict['displays']['beauty']['params']['displayChannels'].append(chan) - if not rman_scene.bl_scene.renderman.use_ai_denoiser: + if rman_scene.bl_scene.renderman.use_legacy_denoiser: filePath = dspys_dict['displays']['beauty']['filePath'] f,ext = os.path.splitext(filePath) dspys_dict['displays']['beauty']['filePath'] = f + '_variance' + ext diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 50b309dc..0eb8650f 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1526,12 +1526,12 @@ { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "Denoising", - "name": "use_ai_denoiser", - "label": "Use AI Denoiser", + "name": "use_legacy_denoiser", + "label": "Use Legacy Denoiser", "type": "int", "default": 0, "widget": "checkbox", - "help": "Use the AI Denoiser to denoise passes." + "help": "Use the previous denoiser to denoise passes." }, { "panel": "RENDER_PT_renderman_spooling_export_options", @@ -1545,8 +1545,8 @@ "help": "Controls the asymmetry value, 0 is best quality, higher values encourage the denoiser to avoid overblurring, leading to weaker denoising.", "conditionalVisOps": { "conditionalVisOp": "notEqualTo", - "conditionalVisPath": "use_ai_denoiser", - "conditionalVisValue": "0" + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "1" } }, { @@ -1560,8 +1560,8 @@ "help": "Enable versbose output.", "conditionalVisOps": { "conditionalVisOp": "notEqualTo", - "conditionalVisPath": "use_ai_denoiser", - "conditionalVisValue": "0" + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "1" } }, { @@ -1574,8 +1574,8 @@ "help": "Inserts a string of custom commands arguments into the denoising process (hyperion), if selected", "conditionalVisOps": { "conditionalVisOp": "notEqualTo", - "conditionalVisPath": "use_ai_denoiser", - "conditionalVisValue": "1" + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "0" } }, { @@ -1588,9 +1588,9 @@ "widget": "checkbox", "help": "The denoiser (hyperion) will attempt to use the GPU (if available)", "conditionalVisOps": { - "conditionalVis1Path": "use_ai_denoiser", + "conditionalVis1Path": "use_legacy_denoiser", "conditionalVis2Op": "notEqualTo", - "conditionalVis1Value": "0", + "conditionalVis1Value": "1", "conditionalVisOp": "and", "conditionalVis1Op": "equalTo", "conditionalVisLeft": "conditionalVis1", diff --git a/rman_spool.py b/rman_spool.py index 70b329d6..6c6caa58 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -221,7 +221,7 @@ def generate_rib_render_tasks(self, anim, parent_task, tasktitle, parent_task.addChild(renderframestask) def generate_aidenoise_tasks(self, start, last, by): - tasktitle = "AI Denoiser Renders" + tasktitle = "Denoiser Renders" parent_task = author.Task() parent_task.title = tasktitle rm = self.bl_scene.renderman @@ -243,17 +243,16 @@ def generate_aidenoise_tasks(self, start, last, by): # for crossframe, do it all in one task cur_frame = self.rman_scene.bl_frame_current task = author.Task() - task.title = 'AI Denoise Cross Frame' + task.title = 'Denoise Cross Frame' command = author.Command(local=False, service="PixarRender") - command.argv = ["aidenoise_ui"] + command.argv = ["denoise_batch"] command.argv.append('--crossframe') - command.argv.append('run') variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], frame=1, asFilePath=True) - path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + path = os.path.join(os.path.dirname(variance_file), 'denoised') command.argv.append('-a') command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) command.argv.append('-o') @@ -266,7 +265,7 @@ def generate_aidenoise_tasks(self, start, last, by): frame=frame_num, asFilePath=True) - path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + path = os.path.join(os.path.dirname(variance_file), 'denoised') for dspy,params in dspys_dict['displays'].items(): if not params['denoise']: @@ -294,15 +293,14 @@ def generate_aidenoise_tasks(self, start, last, by): asFilePath=True) task = author.Task() - task.title = 'AI Denoise Frame %d' % frame_num + task.title = 'Denoise Frame %d' % frame_num command = author.Command(local=False, service="PixarRender") - command.argv = ["aidenoiser_ui"] + command.argv = ["denoise_batch"] if rm.ai_denoiser_verbose: command.argv.append('-v') - command.argv.append('run') - path = os.path.join(os.path.dirname(variance_file), 'denoiosed') + path = os.path.join(os.path.dirname(variance_file), 'denoised') command.argv.append('-a') command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) command.argv.append('-o') @@ -331,7 +329,7 @@ def generate_aidenoise_tasks(self, start, last, by): def generate_denoise_tasks(self, start, last, by): - tasktitle = "Denoiser Renders" + tasktitle = "Denoiser (Legacy) Renders" parent_task = author.Task() parent_task.title = tasktitle rm = self.bl_scene.renderman @@ -361,10 +359,10 @@ def generate_denoise_tasks(self, start, last, by): # for crossframe, do it all in one task cur_frame = self.rman_scene.bl_frame_current task = author.Task() - task.title = 'Denoise Cross Frame' + task.title = 'Denoise (Legacy) Cross Frame' command = author.Command(local=False, service="PixarRender") - command.argv = ["denoise"] + command.argv = ["denoise_legacy"] for opt in denoise_options: command.argv.append(opt) command.argv.append('--crossframe') @@ -422,10 +420,10 @@ def generate_denoise_tasks(self, start, last, by): continue task = author.Task() - task.title = 'Denoise Frame %d' % frame_num + task.title = 'Denoise (Legacy) Frame %d' % frame_num command = author.Command(local=False, service="PixarRender") - command.argv = ["denoise"] + command.argv = ["denoise_legacy"] for opt in denoise_options: command.argv.append(opt) if dspy == 'beauty': @@ -496,10 +494,11 @@ def blender_batch_render(self, bl_filename): # Don't generate denoise tasks if we're baking # or using the Blender compositor if rm.hider_type == 'RAYTRACE' and (not rm.use_bl_compositor or not scene.use_nodes): - if rm.use_ai_denoiser: - parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) - else: + if rm.use_legacy_denoiser: parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) + else: + parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) + job.addChild(parent_task) scene_filename = bpy.data.filepath @@ -579,10 +578,11 @@ def batch_render(self): # Don't denoise if we're baking if rm.hider_type == 'RAYTRACE': - if rm.use_ai_denoiser: - parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) - else: + if rm.use_legacy_denoiser: parent_task = self.generate_denoise_tasks(frame_begin, frame_end, by) + else: + parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) + job.addChild(parent_task) bl_filename = bpy.data.filepath From 219e1ae7d46d27a52b49398d9f119edc256109a0 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 27 Feb 2023 16:07:08 -0800 Subject: [PATCH 239/278] More changes to support AI denoiser: * there's now only a global control for single frame/cross frame denoising. * for spooling, the only difference between single and cross frame denoising is now just a switch. --- rman_config/config/rman_properties_scene.json | 20 ++- rman_spool.py | 147 +++++++----------- rman_ui/rman_ui_aovs.py | 7 +- 3 files changed, 78 insertions(+), 96 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 0eb8650f..01967e65 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -63,7 +63,7 @@ "type": "int", "default": 0, "widget": "checkbox", - "help": "Denoise with OptiX denoiser", + "help": "Denoise Blender render displays with OptiX denoiser", "update_function_name": "update_blender_optix_denoiser", "update_function": "def update_blender_optix_denoiser(self, context):\n from ..rman_render import RmanRender\n rr = RmanRender.get_rman_render()\n rr.rman_scene_sync.update_displays(context)", "conditionalVisOps": { @@ -1548,7 +1548,23 @@ "conditionalVisPath": "use_legacy_denoiser", "conditionalVisValue": "1" } - }, + }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", + "name": "ai_denoiser_mode", + "label": "Mode", + "type": "string", + "default": "singleframe", + "widget": "mapper", + "options": "Single Frame:singleframe|Cross Frame:crossframe", + "help": "Use single frame or cross frame. Cross frame requires at least 7 frames.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "1" + } + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "Denoising", diff --git a/rman_spool.py b/rman_spool.py index 6c6caa58..61117280 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -224,105 +224,68 @@ def generate_aidenoise_tasks(self, start, last, by): tasktitle = "Denoiser Renders" parent_task = author.Task() parent_task.title = tasktitle - rm = self.bl_scene.renderman - - # any cross frame? - do_cross_frame = False + rm = self.bl_scene.renderman dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) - for dspy,params in dspys_dict['displays'].items(): + + img_files = list() + have_variance = False + for frame_num in range(start, last + 1, by): + self.rman_render.bl_frame_current = frame_num + + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=frame_num, + asFilePath=True) + + path = os.path.join(os.path.dirname(variance_file), 'denoised') + + for dspy,params in dspys_dict['displays'].items(): if not params['denoise']: - continue - if params['denoise_mode'] == 'crossframe': - do_cross_frame = True - break + continue + + if dspy == 'beauty': + if not have_variance: + have_variance = True + img_files.append(variance_file) + else: + aov_file = string_utils.expand_string(params['filePath'], + frame=frame_num, + asFilePath=True) + img_files.append(aov_file) + + if not have_variance or len(img_files) < 1: + return None + + do_cross_frame = (rm.ai_denoiser_mode == 'crossframe') + if start == last: do_cross_frame = False + cur_frame = self.rman_scene.bl_frame_current + task = author.Task() if do_cross_frame: - # for crossframe, do it all in one task - cur_frame = self.rman_scene.bl_frame_current - task = author.Task() - task.title = 'Denoise Cross Frame' - command = author.Command(local=False, service="PixarRender") - - command.argv = ["denoise_batch"] - - command.argv.append('--crossframe') - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], - frame=1, - asFilePath=True) - path = os.path.join(os.path.dirname(variance_file), 'denoised') - command.argv.append('-a') - command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) - command.argv.append('-o') - command.argv.append(path) - - for frame_num in range(start, last + 1, by): - self.rman_render.bl_frame_current = frame_num - - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], - frame=frame_num, - asFilePath=True) - - path = os.path.join(os.path.dirname(variance_file), 'denoised') - - for dspy,params in dspys_dict['displays'].items(): - if not params['denoise']: - continue - - if dspy == 'beauty': - command.argv.append(variance_file) - else: - aov_file = string_utils.expand_string(params['filePath'], - frame=frame_num, - asFilePath=True) - command.argv.append(aov_file) - - task.addCommand(command) - parent_task.addChild(task) - + task.title = 'Denoise Frames %d - %d (Cross Frame)' % (start, last) else: - # single frame - cur_frame = self.rman_scene.bl_frame_current - for frame_num in range(start, last + 1, by): - self.rman_render.bl_frame_current = frame_num - - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], - frame=frame_num, - asFilePath=True) - - task = author.Task() - task.title = 'Denoise Frame %d' % frame_num - command = author.Command(local=False, service="PixarRender") - - command.argv = ["denoise_batch"] - - if rm.ai_denoiser_verbose: - command.argv.append('-v') - path = os.path.join(os.path.dirname(variance_file), 'denoised') - command.argv.append('-a') - command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) - command.argv.append('-o') - command.argv.append(path) + task.title = 'Denoise Frames (%d - %d)' % (start, last) + + command = author.Command(local=False, service="PixarRender") - for dspy,params in dspys_dict['displays'].items(): - if not params['denoise']: - continue - - if params['denoise_mode'] != 'singleframe': - continue + command.argv = ["denoise_batch"] - if dspy == 'beauty': - command.argv.append(variance_file) - else: - aov_file = string_utils.expand_string(params['filePath'], - frame=frame_num, - asFilePath=True) - command.argv.append(aov_file) - - task.addCommand(command) - parent_task.addChild(task) + if do_cross_frame: + command.argv.append('--crossframe') + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=1, + asFilePath=True) + path = os.path.join(os.path.dirname(variance_file), 'denoised') + command.argv.append('-a') + command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) + command.argv.append('-o') + command.argv.append(path) + command.argv.extend(img_files) + + task.addCommand(command) + parent_task.addChild(task) self.rman_render.bl_frame_current = cur_frame return parent_task @@ -499,7 +462,8 @@ def blender_batch_render(self, bl_filename): else: parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) - job.addChild(parent_task) + if parent_task: + job.addChild(parent_task) scene_filename = bpy.data.filepath if scene_filename == '': @@ -583,7 +547,8 @@ def batch_render(self): else: parent_task = self.generate_aidenoise_tasks(frame_begin, frame_end, by) - job.addChild(parent_task) + if parent_task: + job.addChild(parent_task) bl_filename = bpy.data.filepath if bl_filename == '': diff --git a/rman_ui/rman_ui_aovs.py b/rman_ui/rman_ui_aovs.py index c8ed74da..83bd9a0d 100644 --- a/rman_ui/rman_ui_aovs.py +++ b/rman_ui/rman_ui_aovs.py @@ -256,9 +256,10 @@ def draw_item(self, layout, context, item): # denoise options row = col.row() row.prop(item, 'denoise') - row = col.row() - row.enabled = item.denoise - row.prop(item, 'denoise_mode') + if rm.use_legacy_denoiser: + row = col.row() + row.enabled = item.denoise + row.prop(item, 'denoise_mode') row = col.row() row.label(text='') From 0eaca555403f2e6d49625e92996c3410bc475c09 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 1 Mar 2023 13:17:12 -0800 Subject: [PATCH 240/278] Fix spooling of AOV denoising tasks. We weren't correctly substituting the token. --- rman_spool.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rman_spool.py b/rman_spool.py index 61117280..ad67d520 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -236,19 +236,18 @@ def generate_aidenoise_tasks(self, start, last, by): frame=frame_num, asFilePath=True) - path = os.path.join(os.path.dirname(variance_file), 'denoised') - for dspy,params in dspys_dict['displays'].items(): if not params['denoise']: continue - if dspy == 'beauty': if not have_variance: have_variance = True img_files.append(variance_file) else: + token_dict = {'aov': dspy} aov_file = string_utils.expand_string(params['filePath'], frame=frame_num, + token_dict=token_dict, asFilePath=True) img_files.append(aov_file) @@ -352,8 +351,10 @@ def generate_denoise_tasks(self, start, last, by): command.argv.append(variance_file) else: command.argv.append(variance_file) + token_dict = {'aov': dspy} aov_file = string_utils.expand_string(params['filePath'], frame=frame_num, + token_dict=token_dict, asFilePath=True) command.argv.append(aov_file) @@ -393,8 +394,10 @@ def generate_denoise_tasks(self, start, last, by): command.argv.append(variance_file) else: command.argv.append(variance_file) + token_dict = {'aov': dspy} aov_file = string_utils.expand_string(params['filePath'], frame=frame_num, + token_dict=token_dict, asFilePath=True) command.argv.append(aov_file) From a5db51b99b8d2a321dea5e723873b15c62cd2d60 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Mar 2023 14:52:16 -0800 Subject: [PATCH 241/278] Hide the use_legacy_denoiser option. We want users to use the new denoiser. However, for beta 2 Windows and mac will use the legacy denoiser. --- rman_config/config/rman_properties_scene.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 01967e65..0b963679 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1524,14 +1524,16 @@ "help": "Inserts a string of custom command arguments into the render process" }, { - "panel": "RENDER_PT_renderman_spooling_export_options", + "panel": "", "page": "Denoising", "name": "use_legacy_denoiser", "label": "Use Legacy Denoiser", "type": "int", "default": 0, "widget": "checkbox", - "help": "Use the previous denoiser to denoise passes." + "help": "Use the previous denoiser to denoise passes.", + "get_function_name": "get_use_legacy_denoiser", + "get_function": "def get_use_legacy_denoiser(self):\n import sys\n if sys.platform=='linux':\n return 0\n return 1" }, { "panel": "RENDER_PT_renderman_spooling_export_options", From 3b79563abce10b4f1f042fc5b0f51d06f1ce0161 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Mar 2023 15:08:27 -0800 Subject: [PATCH 242/278] Remove the get_functions on use_legacy_denoiser. --- rman_config/config/rman_properties_scene.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 0b963679..459e183e 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1531,9 +1531,7 @@ "type": "int", "default": 0, "widget": "checkbox", - "help": "Use the previous denoiser to denoise passes.", - "get_function_name": "get_use_legacy_denoiser", - "get_function": "def get_use_legacy_denoiser(self):\n import sys\n if sys.platform=='linux':\n return 0\n return 1" + "help": "Use the previous denoiser to denoise passes." }, { "panel": "RENDER_PT_renderman_spooling_export_options", From 1994ca3adb2b514bd462c43ffcc2265604359440 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 3 Mar 2023 15:23:46 -0800 Subject: [PATCH 243/278] Bump version strings for beta 2. --- __init__.py | 2 +- rman_constants.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 7bcf1e57..2429f9ef 100644 --- a/__init__.py +++ b/__init__.py @@ -35,7 +35,7 @@ "location": "Render Properties > Render Engine > RenderMan", "description": "RenderMan 25 integration", "doc_url": "https://rmanwiki.pixar.com/display/RFB", - "warning": "BETA 1", + "warning": "BETA 2", "category": "Render"} __RMAN_ADDON_LOADED__ = False diff --git a/rman_constants.py b/rman_constants.py index c38ba03f..0c23d685 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -2,8 +2,8 @@ import bpy import sys -RFB_ADDON_VERSION_MAJOR = 24 -RFB_ADDON_VERSION_MINOR = 4 +RFB_ADDON_VERSION_MAJOR = 25 +RFB_ADDON_VERSION_MINOR = 0 RFB_ADDON_VERSION_PATCH = 0 RFB_ADDON_VERSION = (RFB_ADDON_VERSION_MAJOR, RFB_ADDON_VERSION_MINOR, RFB_ADDON_VERSION_PATCH) RFB_ADDON_VERSION_STRING = '%d.%d.%d' % (RFB_ADDON_VERSION_MAJOR, RFB_ADDON_VERSION_MINOR, RFB_ADDON_VERSION_PATCH) From 2fa2b34df5ef142beef1c0dac55ce1249a7de687 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Mar 2023 07:06:16 -0800 Subject: [PATCH 244/278] Add the verbose flag for the aidenoiser, if it's turned on by the user. --- rman_spool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_spool.py b/rman_spool.py index ad67d520..a1c21451 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -277,6 +277,8 @@ def generate_aidenoise_tasks(self, start, last, by): frame=1, asFilePath=True) path = os.path.join(os.path.dirname(variance_file), 'denoised') + if rm.ai_denoiser_verbose: + command.argv.append('-v') command.argv.append('-a') command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) command.argv.append('-o') From 372b56e010279b3dd23a83739ff9171cfdbd408a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Mar 2023 07:21:48 -0800 Subject: [PATCH 245/278] Add a missing option (jitter) and a missing primvar (volume:dsovelocity) --- rman_config/config/rman_properties_scene.json | 10 ++++++++++ rman_config/config/rman_properties_volume.json | 15 ++++++++++++++- rman_translators/rman_openvdb_translator.py | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index 459e183e..fe2bbb7a 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -586,6 +586,16 @@ "widget": "checkbox", "help": "Incremental mode means the renderer visits every pixel in the image, computing a single sample for each, then does the same for a second sample, etc. In non-incremental mode, the renderer visits each pixel once and computes all the samples in one go." }, + { + "panel": "RENDER_PT_renderman_sampling", + "name": "hider_jitter", + "riopt": "hider:jitter", + "label": "Jitter", + "type": "int", + "default": 1, + "widget": "checkbox", + "help": "This option toggles the jitter (variation) in camera samples. By default, this hider option is enabled (set to 1), meaning that camera samples will be placed randomly in a pixel location. When it is turned off, camera rays are placed in the center of the pixel. When turned off, surface edges will appear aliased and jagged." + }, { "panel": "RENDER_PT_renderman_sampling", "page": "Global Trace Settings", diff --git a/rman_config/config/rman_properties_volume.json b/rman_config/config/rman_properties_volume.json index 9fd12013..560011ad 100644 --- a/rman_config/config/rman_properties_volume.json +++ b/rman_config/config/rman_properties_volume.json @@ -43,6 +43,19 @@ "page": "", "help": "Currently only used for aggregate volumes, and only for volumes that use an ImplicitField DSO. If set to 1, the DSO may be able to use stored information from the file directly to compute the minimum and maximum values within the volume. This may allow the renderer to avoid shading the volume up front, greatly decreasing time to first pixel. This can only be enabled if the density signal from the volume is used directly, or if the density signal is modulated only by the DSO itself. Any shading modifications of the density signal requires setting this parameter to off.", "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_dsominmax', c)" - } + }, + { + "panel": "VOLUME_PT_renderman_openvdb_attributes", + "name": "volume_dsovelocity", + "label": "DSO Velocity", + "type": "int", + "ipr_editable": true, + "default": 0, + "widget": "checkbox", + "primvar": "volume:dsovelocity", + "page": "", + "help": "Used only for aggregate volumes that use an ImplicitField DSO. If set to 1, the DSO can provide velocity bounds to the renderer directly via the BoxMotion call. This can allow the renderer to avoid upfront shading of the volume's velocity data when using Eulerian velocity, improving render time and time to first pixel greatly. This can only be enabled if the velocity signal from the DSO is used directly.", + "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_dsovelocity', c)" + } ] } \ No newline at end of file diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index 6ce05b26..82d64319 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -109,5 +109,6 @@ def update(self, ob, rman_sg_openvdb): scenegraph_utils.export_vol_aggregate(self.rman_scene.bl_scene, primvar, ob) primvar.SetInteger("volume:dsominmax", rm.volume_dsominmax) + primvar.SetInteger("volume:dsovelocity", rm.volume_dsovelocity) super().export_object_primvars(ob, primvar) rman_sg_openvdb.sg_node.SetPrimVars(primvar) \ No newline at end of file From 66e0389e87e61f9e0e3be91da50677bc1f9cd7ac Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 8 Mar 2023 08:06:32 -0800 Subject: [PATCH 246/278] Add flow and output dir options to aidenoiser. --- rman_config/config/rman_properties_scene.json | 30 +++++++++++++++++++ rman_spool.py | 10 +++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/rman_config/config/rman_properties_scene.json b/rman_config/config/rman_properties_scene.json index fe2bbb7a..f7c5a75b 100644 --- a/rman_config/config/rman_properties_scene.json +++ b/rman_config/config/rman_properties_scene.json @@ -1559,6 +1559,21 @@ "conditionalVisValue": "1" } }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", + "name": "ai_denoiser_output_dir", + "label": "Output Directory", + "type": "string", + "widget": "dirinput", + "default": "", + "help": "The output directory for the denoised images. If not set, we put the images into a subfolder 'denoised' relative to the input images.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "1" + } + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "Denoising", @@ -1575,6 +1590,21 @@ "conditionalVisValue": "1" } }, + { + "panel": "RENDER_PT_renderman_spooling_export_options", + "page": "Denoising", + "name": "ai_denoiser_flow", + "label": "Flow", + "type": "int", + "default": 1, + "widget": "checkbox", + "help": "Whether to compute optical flow.", + "conditionalVisOps": { + "conditionalVisOp": "notEqualTo", + "conditionalVisPath": "use_legacy_denoiser", + "conditionalVisValue": "1" + } + }, { "panel": "RENDER_PT_renderman_spooling_export_options", "page": "Denoising", diff --git a/rman_spool.py b/rman_spool.py index a1c21451..6ffa1793 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -3,6 +3,7 @@ import getpass import socket import bpy +from .rfb_utils import filepath_utils from .rfb_utils import string_utils from .rfb_utils.envconfig_utils import envconfig from .rfb_utils import display_utils @@ -276,13 +277,18 @@ def generate_aidenoise_tasks(self, start, last, by): variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], frame=1, asFilePath=True) - path = os.path.join(os.path.dirname(variance_file), 'denoised') + if rm.ai_denoiser_verbose: command.argv.append('-v') command.argv.append('-a') command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) command.argv.append('-o') - command.argv.append(path) + path = filepath_utils.get_real_path(rm.ai_denoiser_output_dir) + if not os.path.exists(path): + path = os.path.join(os.path.dirname(variance_file), 'denoised') + command.argv.append(path) + if rm.ai_denoiser_flow: + command.argv.append('-f') command.argv.extend(img_files) task.addCommand(command) From 4c75aaec5b94bb784145838fabfb0a51cfe529b4 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 9 Mar 2023 09:40:47 -0800 Subject: [PATCH 247/278] Rename "External Rendering" to "Batch Rendering". --- rman_operators/rman_operators_render.py | 28 ++++++++++++------------- rman_ui/rman_ui_render_panels.py | 10 ++++----- rman_ui/rman_ui_view3d_panels.py | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rman_operators/rman_operators_render.py b/rman_operators/rman_operators_render.py index 53710f54..160ff198 100644 --- a/rman_operators/rman_operators_render.py +++ b/rman_operators/rman_operators_render.py @@ -94,10 +94,10 @@ def invoke(self, context, event=None): context.window_manager.fileselect_add(self) return{'RUNNING_MODAL'} -class PRMAN_OT_ExternalRendermanBake(bpy.types.Operator): - bl_idname = "renderman.external_bake" - bl_label = "External Baking" - bl_description = "Spool an external bake render." +class PRMAN_OT_BatchRendermanBake(bpy.types.Operator): + bl_idname = "renderman.batch_bake_render" + bl_label = "Batch Baking" + bl_description = "Spool a batch bake render." bl_options = {'INTERNAL'} def execute(self, context): @@ -118,15 +118,15 @@ def execute(self, context): return {'FINISHED'} -class PRMAN_OT_ExternalRender(bpy.types.Operator): +class PRMAN_OT_BatchRender(bpy.types.Operator): '''''' - bl_idname = "renderman.external_render" - bl_label = "External Render" - bl_description = "Launch a spooled external render." + bl_idname = "renderman.batch_render" + bl_label = "Batch Render" + bl_description = "Launch a spooled batch render." bl_options = {'INTERNAL'} - def external_blender_batch(self, context): + def blender_batch_render(self, context): rm = context.scene.renderman if rm.queuing_system != 'none': from .. import rman_spool @@ -162,7 +162,7 @@ def external_blender_batch(self, context): else: self.report({'ERROR'}, 'Queuing system set to none') - def external_rib_render(self, context): + def rib_batch_render(self, context): scene = context.scene rm = scene.renderman if not rm.is_rman_interactive_running: @@ -180,9 +180,9 @@ def execute(self, context): rm = scene.renderman if not rm.is_rman_interactive_running: if scene.renderman.spool_style == 'rib': - self.external_rib_render(context) + self.rib_batch_render(context) else: - self.external_blender_batch(context) + self.blender_batch_render(context) else: self.report({"ERROR"}, "Viewport rendering is on.") return {'FINISHED'} @@ -324,8 +324,8 @@ def invoke(self, context, event=None): PRMAN_OT_Renderman_Use_Renderman, PRMAN_OT_RendermanBake, PRMAN_OT_RendermanBakeSelectedBrickmap, - PRMAN_OT_ExternalRendermanBake, - PRMAN_OT_ExternalRender, + PRMAN_OT_BatchRendermanBake, + PRMAN_OT_BatchRender, PRMAN_OT_StartInteractive, PRMAN_OT_StopInteractive, PRMAN_OT_StopRender, diff --git a/rman_ui/rman_ui_render_panels.py b/rman_ui/rman_ui_render_panels.py index 0dbefa3e..29eb0cc7 100644 --- a/rman_ui/rman_ui_render_panels.py +++ b/rman_ui/rman_ui_render_panels.py @@ -58,7 +58,7 @@ def draw(self, context): _draw_ui_from_rman_config('rman_properties_scene', 'RENDER_PT_renderman_render', context, layout, rm) class RENDER_PT_renderman_spooling(PRManButtonsPanel, Panel): - bl_label = "External Rendering" + bl_label = "Batch Rendering" bl_options = {'DEFAULT_CLOSED'} def draw(self, context): @@ -75,11 +75,11 @@ def draw(self, context): col = layout.column() row = col.row(align=True) rman_batch = rfb_icons.get_icon("rman_batch") - row.operator("renderman.external_render", - text="External Render", icon_value=rman_batch.icon_id) + row.operator("renderman.batch_render", + text="Batch Render", icon_value=rman_batch.icon_id) rman_bake = rfb_icons.get_icon("rman_bake") - row.operator("renderman.external_bake", - text="External Bake Render", icon_value=rman_bake.icon_id) + row.operator("renderman.batch_bake_render", + text="Batch Bake Render", icon_value=rman_bake.icon_id) # do animation col.prop(rm, 'external_animation') diff --git a/rman_ui/rman_ui_view3d_panels.py b/rman_ui/rman_ui_view3d_panels.py index 3fb7872a..75df6d35 100644 --- a/rman_ui/rman_ui_view3d_panels.py +++ b/rman_ui/rman_ui_view3d_panels.py @@ -78,7 +78,7 @@ def draw(self, context): row = layout.row(align=True) rman_batch = rfb_icons.get_icon("rman_batch") - row.operator("renderman.external_render", + row.operator("renderman.batch_render", text="External Render", icon_value=rman_batch.icon_id) row.prop(context.scene, "rm_render_external", text="", From 962be99d5c4f56cc0b10e39bea3a66095b09056b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 13 Mar 2023 10:23:38 -0700 Subject: [PATCH 248/278] Switch to using "it" instead of "sho" for the preview image tasks in alf job. --- rman_spool.py | 52 +++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/rman_spool.py b/rman_spool.py index 6ffa1793..646caa50 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -64,7 +64,7 @@ def add_prman_render_task(self, parentTask, title, threads, rib, img): task = author.Task() task.title = title if img: - task.preview = 'sho %s' % str(img) + task.preview = '%s %s' % (envconfig().rman_it_path, str(img)) command = author.Command(local=False, service="PixarRender") command.argv = ["prman"] @@ -83,14 +83,11 @@ def add_prman_render_task(self, parentTask, title, threads, rib, img): task.addCommand(command) parentTask.addChild(task) - def add_blender_render_task(self, frame, parentTask, title, bl_filename, img, chunk=None): + def add_blender_render_task(self, frame, parentTask, title, bl_filename, chunk=None): rm = self.bl_scene.renderman task = author.Task() task.title = title - if img: - img_expanded = string_utils.expand_string(img, frame=frame, asFilePath=True) - task.preview = 'sho %s' % img_expanded command = author.Command(local=False, service="PixarRender") bl_blender_path = bpy.app.binary_path @@ -98,25 +95,33 @@ def add_blender_render_task(self, frame, parentTask, title, bl_filename, img, ch command.argv.append('-b') command.argv.append('%%D(%s)' % bl_filename) + begin = frame + end = frame if chunk: command.argv.append('-f') command.argv.append('%s..%s' % (str(frame), str(frame+(chunk)))) + end = frame+chunk else: command.argv.append('-f') command.argv.append(str(frame)) task.addCommand(command) + + imgs = list() + dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) + for i in range(begin, end+1): + img = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=i, + asFilePath=True) + imgs.append(img) + + + task.preview = "%s %s" % (envconfig().rman_it_path, " ".join(imgs)) parentTask.addChild(task) def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, start, last, by, chunk, bl_filename): - - self.any_denoise = display_utils.any_dspys_denoise(self.rman_scene.bl_view_layer) - img = None - if not self.any_denoise: - dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) - img = dspys_dict['displays']['beauty']['filePath'] - + if anim is False: frametasktitle = ("%s Frame: %d " % @@ -128,7 +133,7 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, prmantasktitle = "%s (render)" % frametasktitle self.add_blender_render_task(start, frametask, prmantasktitle, - bl_filename, img) + bl_filename) parent_task.addChild(frametask) @@ -151,7 +156,7 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, (tasktitle, iframe, iframe+diff)) self.add_blender_render_task(iframe, renderframestask, prmantasktitle, - bl_filename, None, chunk=diff) + bl_filename, chunk=diff) iframe += diff+1 else: for iframe in range(start, last + 1, by): @@ -159,7 +164,7 @@ def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, (tasktitle, int(iframe))) self.add_blender_render_task(iframe, renderframestask, prmantasktitle, - bl_filename, img) + bl_filename) parent_task.addChild(renderframestask) @@ -229,7 +234,14 @@ def generate_aidenoise_tasks(self, start, last, by): dspys_dict = display_utils.get_dspy_dict(self.rman_scene, expandTokens=False) img_files = list() + preview_img_files = list() have_variance = False + variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + frame=1, + asFilePath=True) + path = filepath_utils.get_real_path(rm.ai_denoiser_output_dir) + if not os.path.exists(path): + path = os.path.join(os.path.dirname(variance_file), 'denoised') for frame_num in range(start, last + 1, by): self.rman_render.bl_frame_current = frame_num @@ -244,6 +256,7 @@ def generate_aidenoise_tasks(self, start, last, by): if not have_variance: have_variance = True img_files.append(variance_file) + preview_img_files.append(os.path.join(path, os.path.basename(variance_file))) else: token_dict = {'aov': dspy} aov_file = string_utils.expand_string(params['filePath'], @@ -273,25 +286,20 @@ def generate_aidenoise_tasks(self, start, last, by): command.argv = ["denoise_batch"] if do_cross_frame: - command.argv.append('--crossframe') - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], - frame=1, - asFilePath=True) + command.argv.append('--crossframe') if rm.ai_denoiser_verbose: command.argv.append('-v') command.argv.append('-a') command.argv.append('%.3f' % rm.ai_denoiser_asymmetry) command.argv.append('-o') - path = filepath_utils.get_real_path(rm.ai_denoiser_output_dir) - if not os.path.exists(path): - path = os.path.join(os.path.dirname(variance_file), 'denoised') command.argv.append(path) if rm.ai_denoiser_flow: command.argv.append('-f') command.argv.extend(img_files) task.addCommand(command) + task.preview = "%s %s" % (envconfig().rman_it_path, " ".join(preview_img_files)) parent_task.addChild(task) self.rman_render.bl_frame_current = cur_frame From 3e7975bea5cb58b6db261572ea6e1ad34d87fe39 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 14 Mar 2023 08:40:00 -0700 Subject: [PATCH 249/278] Fixes for preview commands in the alf job for Windows. Make sure the path to "it" is in quotes, to deal with spaces. --- rman_spool.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/rman_spool.py b/rman_spool.py index 646caa50..c9fd8559 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -2,6 +2,7 @@ import os import getpass import socket +import platform import bpy from .rfb_utils import filepath_utils from .rfb_utils import string_utils @@ -56,6 +57,17 @@ def _add_additional_prman_args(self, args): args.append('%r') scene_utils.set_render_variant_spool(self.bl_scene, args, self.is_tractor) + + def add_preview_task(self, task, imgs): + if imgs is None: + return + it_path = envconfig().rman_it_path + if platform.system() == 'Windows': + it_path = '"%s"' % it_path + img = imgs + if isinstance(imgs, list): + img = " ".join(imgs) + task.preview = "%s %s" % (it_path, img) def add_prman_render_task(self, parentTask, title, threads, rib, img): rm = self.bl_scene.renderman @@ -63,8 +75,7 @@ def add_prman_render_task(self, parentTask, title, threads, rib, img): task = author.Task() task.title = title - if img: - task.preview = '%s %s' % (envconfig().rman_it_path, str(img)) + self.add_preview_task(task, img) command = author.Command(local=False, service="PixarRender") command.argv = ["prman"] @@ -116,7 +127,7 @@ def add_blender_render_task(self, frame, parentTask, title, bl_filename, chunk=N imgs.append(img) - task.preview = "%s %s" % (envconfig().rman_it_path, " ".join(imgs)) + self.add_preview_task(task, imgs) parentTask.addChild(task) def generate_blender_batch_tasks(self, anim, parent_task, tasktitle, @@ -299,7 +310,7 @@ def generate_aidenoise_tasks(self, start, last, by): command.argv.extend(img_files) task.addCommand(command) - task.preview = "%s %s" % (envconfig().rman_it_path, " ".join(preview_img_files)) + self.add_preview_task(task, preview_img_files) parent_task.addChild(task) self.rman_render.bl_frame_current = cur_frame From 208ae418feb2934b2ec27e5a3a76017776dcef73 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 14 Mar 2023 09:27:05 -0700 Subject: [PATCH 250/278] Move the sampling panel higher. Suggested by eugene. --- rman_ui/rman_ui_render_panels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_ui/rman_ui_render_panels.py b/rman_ui/rman_ui_render_panels.py index 29eb0cc7..a691a12f 100644 --- a/rman_ui/rman_ui_render_panels.py +++ b/rman_ui/rman_ui_render_panels.py @@ -364,11 +364,11 @@ def draw(self, context): RENDER_PT_renderman_render, RENDER_PT_renderman_spooling, RENDER_PT_renderman_spooling_export_options, + RENDER_PT_renderman_sampling, RENDER_PT_renderman_baking, RENDER_PT_renderman_world_integrators, RENDER_PT_renderman_world_display_filters, RENDER_PT_renderman_world_sample_filters, - RENDER_PT_renderman_sampling, RENDER_PT_renderman_motion_blur, RENDER_PT_renderman_advanced_settings, RENDER_PT_renderman_custom_options From 63ab1c5b41b5c987e84a863bfad4d34dd23bc651 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 15 Mar 2023 13:49:15 -0700 Subject: [PATCH 251/278] In the RendermanOutputNode update method, double check that the solo node still exists. It might have been deleted or rename. In that case, reset. --- rfb_utils/shadergraph_utils.py | 53 +++++++++++++++++++++++++ rman_bl_nodes/rman_bl_nodes_ops.py | 55 +++----------------------- rman_bl_nodes/rman_bl_nodes_shaders.py | 14 ++++++- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/rfb_utils/shadergraph_utils.py b/rfb_utils/shadergraph_utils.py index bb47ee66..0c19b99b 100644 --- a/rfb_utils/shadergraph_utils.py +++ b/rfb_utils/shadergraph_utils.py @@ -2,6 +2,7 @@ from . import filepath_utils from . import string_utils from . import object_utils +from .prefs_utils import get_pref from ..rman_constants import RMAN_STYLIZED_FILTERS, RMAN_STYLIZED_PATTERNS, RMAN_UTILITY_PATTERN_NAMES, RFB_FLOAT3 import math import bpy @@ -258,6 +259,58 @@ def is_socket_float3_type(socket): else: return socket.type in ['RGBA', 'VECTOR'] +def set_solo_node(node, nt, solo_node_name, refresh_solo=False, solo_node_output=''): + def hide_all(nt, node): + if not get_pref('rman_solo_collapse_nodes'): + return + for n in nt.nodes: + hide = (n != node) + if hasattr(n, 'prev_hidden'): + setattr(n, 'prev_hidden', n.hide) + n.hide = hide + for input in n.inputs: + if not input.is_linked: + if hasattr(input, 'prev_hidden'): + setattr(input, 'prev_hidden', input.hide) + input.hide = hide + + for output in n.outputs: + if not output.is_linked: + if hasattr(output, 'prev_hidden'): + setattr(output, 'prev_hidden', output.hide) + output.hide = hide + + def unhide_all(nt): + if not get_pref('rman_solo_collapse_nodes'): + return + for n in nt.nodes: + hide = getattr(n, 'prev_hidden', False) + n.hide = hide + for input in n.inputs: + if not input.is_linked: + hide = getattr(input, 'prev_hidden', False) + input.hide = hide + + for output in n.outputs: + if not output.is_linked: + hide = getattr(output, 'prev_hidden', False) + output.hide = hide + + if refresh_solo: + node.solo_nodetree = None + node.solo_node_name = '' + node.solo_node_output = '' + unhide_all(nt) + return + + if solo_node_name: + node.solo_nodetree = nt + node.solo_node_name = solo_node_name + node.solo_node_output = solo_node_output + solo_node = nt.nodes[solo_node_name] + hide_all(nt, solo_node) + + # do we need to convert this socket? def do_convert_socket(from_socket, to_socket): if not to_socket: diff --git a/rman_bl_nodes/rman_bl_nodes_ops.py b/rman_bl_nodes/rman_bl_nodes_ops.py index 83ae078c..153689f5 100644 --- a/rman_bl_nodes/rman_bl_nodes_ops.py +++ b/rman_bl_nodes/rman_bl_nodes_ops.py @@ -5,6 +5,7 @@ from ..rfb_utils.prefs_utils import get_pref from ..rman_constants import RMAN_BL_NODE_DESCRIPTIONS from ..rfb_utils.shadergraph_utils import find_node, find_selected_pattern_node, is_socket_same_type, find_material_from_nodetree +from ..rfb_utils.shadergraph_utils import set_solo_node import bpy import os @@ -524,60 +525,17 @@ class NODE_OT_rman_node_set_solo(bpy.types.Operator): solo_node_name: StringProperty(default="") refresh_solo: BoolProperty(default=False) - def hide_all(self, nt, node): - if not get_pref('rman_solo_collapse_nodes'): - return - for n in nt.nodes: - hide = (n != node) - if hasattr(n, 'prev_hidden'): - setattr(n, 'prev_hidden', n.hide) - n.hide = hide - for input in n.inputs: - if not input.is_linked: - if hasattr(input, 'prev_hidden'): - setattr(input, 'prev_hidden', input.hide) - input.hide = hide - - for output in n.outputs: - if not output.is_linked: - if hasattr(output, 'prev_hidden'): - setattr(output, 'prev_hidden', output.hide) - output.hide = hide - - def unhide_all(self, nt): - if not get_pref('rman_solo_collapse_nodes'): - return - for n in nt.nodes: - hide = getattr(n, 'prev_hidden', False) - n.hide = hide - for input in n.inputs: - if not input.is_linked: - hide = getattr(input, 'prev_hidden', False) - input.hide = hide - - for output in n.outputs: - if not output.is_linked: - hide = getattr(output, 'prev_hidden', False) - output.hide = hide - def invoke(self, context, event): nt = context.nodetree output_node = context.node selected_node = None if self.refresh_solo: - output_node.solo_nodetree = None - output_node.solo_node_name = '' - output_node.solo_node_output = '' - self.unhide_all(nt) + set_solo_node(output_node, nt, '', refresh_solo=True) return {'FINISHED'} if self.solo_node_name: - output_node.solo_nodetree = nt - output_node.solo_node_name = self.solo_node_name - output_node.solo_node_output = '' - solo_node = nt.nodes[self.solo_node_name] - self.hide_all(nt, solo_node) + set_solo_node(output_node, nt, self.solo_node_name, refresh_solo=False) return {'FINISHED'} selected_node = find_selected_pattern_node(nt) @@ -585,11 +543,8 @@ def invoke(self, context, event): if not selected_node: self.report({'ERROR'}, "Pattern node not selected") return {'FINISHED'} - - output_node.solo_nodetree = nt - output_node.solo_node_name = selected_node.name - output_node.solo_node_output = '' - self.hide_all(nt, nt.nodes[selected_node.name]) + + set_solo_node(output_node, nt, selected_node.name, refresh_solo=False) return {'FINISHED'} diff --git a/rman_bl_nodes/rman_bl_nodes_shaders.py b/rman_bl_nodes/rman_bl_nodes_shaders.py index 9e4fd826..4932ea8f 100644 --- a/rman_bl_nodes/rman_bl_nodes_shaders.py +++ b/rman_bl_nodes/rman_bl_nodes_shaders.py @@ -729,16 +729,28 @@ def update(self): pass self.new_links.clear() + + # check if the solo node still exists + if self.solo_node_name: + solo_nodetree = self.solo_nodetree + solo_node = solo_nodetree.nodes.get(self.solo_node_name, None) + if solo_node is None: + shadergraph_utils.set_solo_node(self, solo_nodetree, '', refresh_solo=True) + solo_nodetree.update_tag() + return + + self.id_data.update_tag() # This sucks. There doesn't seem to be a way to tag the material # it needs updating, so we manually issue an edit - + ''' area = getattr(bpy.context, 'area', None) if area and area.type == 'NODE_EDITOR': rr = rman_render.RmanRender.get_rman_render() mat = getattr(bpy.context, 'material', None) if mat: rr.rman_scene_sync.update_material(mat) + ''' class RendermanIntegratorsOutputNode(RendermanShadingNode): bl_label = 'RenderMan Integrators' From 7b3d6b1679dab32c245d2baf4e7b4ad7c2423aca Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 24 Mar 2023 15:16:34 -0700 Subject: [PATCH 252/278] Default to using aggregates for volumes. Since XPU only supports aggregates, we now create a globalVolumeAggregate as a default. Unless tagged otherwise, all volumes go into the global aggregate. If it's already in a different aggregate or the "Globa Volume Aggregate" checkbox is turned off, only then will the volume not be included in the global aggregate. --- rfb_utils/scene_utils.py | 17 ++++++ rfb_utils/scenegraph_utils.py | 11 +++- .../config/rman_properties_object.json | 26 ++++++++- rman_constants.py | 1 + rman_handlers/__init__.py | 2 + .../rman_operators_editors_vol_aggregates.py | 58 +++++++++++++++++-- rman_operators/rman_operators_volumes.py | 4 +- rman_ui/rman_ui_view3d_menus.py | 2 + 8 files changed, 111 insertions(+), 10 deletions(-) diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index a5505d5a..eff436c2 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -2,6 +2,7 @@ from . import object_utils from . import prefs_utils from . import string_utils +from ..rman_constants import RMAN_GLOBAL_VOL_AGGREGATE from ..rfb_logger import rfb_log import bpy import sys @@ -46,6 +47,22 @@ def get_renderman_layer(context): return rm_rl +def add_global_vol_aggregate(): + ''' + Checks to see if the global volume aggregate exists. + If it doesn't exists, we add it. + ''' + bl_scene = bpy.context.scene + rm = bl_scene.renderman + if len(rm.vol_aggregates) > 0: + vol_agg = rm.vol_aggregates[0] + if vol_agg.name == RMAN_GLOBAL_VOL_AGGREGATE: + return + vol_agg = rm.vol_aggregates.add() + vol_agg.name = RMAN_GLOBAL_VOL_AGGREGATE + rm.vol_aggregates.move(len(rm.vol_aggregates)-1, 0) + + def should_use_bl_compositor(bl_scene): ''' Check if we should use the Blender compositor diff --git a/rfb_utils/scenegraph_utils.py b/rfb_utils/scenegraph_utils.py index 706ba12b..2c482872 100644 --- a/rfb_utils/scenegraph_utils.py +++ b/rfb_utils/scenegraph_utils.py @@ -42,11 +42,18 @@ def update_sg_node_primvar(prop_name, context, bl_object=None): def export_vol_aggregate(bl_scene, primvar, ob): vol_aggregate_group = [] - for v in bl_scene.renderman.vol_aggregates: + for i,v in enumerate(bl_scene.renderman.vol_aggregates): + if i == 0: + continue for member in v.members: if member.ob_pointer.original == ob.original: vol_aggregate_group.append(v.name) break if vol_aggregate_group: - primvar.SetStringArray("volume:aggregate", vol_aggregate_group, len(vol_aggregate_group)) \ No newline at end of file + primvar.SetStringArray("volume:aggregate", vol_aggregate_group, len(vol_aggregate_group)) + elif ob.renderman.volume_global_aggregate: + # we assume the first group is the global aggregate + primvar.SetStringArray("volume:aggregate", [bl_scene.renderman.vol_aggregates[0].name], 1) + else: + primvar.SetStringArray("volume:aggregate", [""], 1) \ No newline at end of file diff --git a/rman_config/config/rman_properties_object.json b/rman_config/config/rman_properties_object.json index b5d04c84..3ae30885 100644 --- a/rman_config/config/rman_properties_object.json +++ b/rman_config/config/rman_properties_object.json @@ -862,7 +862,7 @@ "conditionalVisPath": "dice_referenceCameraType", "conditionalVisValue": 1 } - }, + }, { "panel": "OBJECT_PT_renderman_object_geometry_volume", "page": "", @@ -956,7 +956,29 @@ }, "update_function_name": "lambda s, c: update_primvar_func(s, 'volume_velocityshuttercorrection', c)", "help": "The shutter offset used to interpret volumetric velocity. A value of 1 will use the current position of the object and the position of the object on the next frame as the time interval to use for motion blur. A value of -1 will use the position of the object on the previous frame and the current position of the object as the time. A value of 0 will generate an interval which begins halfway through the previous frame and ends halfway into the next frame." - }, + }, + { + "panel": "OBJECT_PT_renderman_object_geometry_volume", + "page": "", + "name": "volume_global_aggregate", + "label": "Global Volume Aggregate", + "type": "int", + "default": 1, + "widget": "checkbox", + "ipr_editable": true, + "conditionalVisOps": { + "conditionalVis1Path": "bl_object_type", + "conditionalVis2Op": "equalTo", + "conditionalVis1Value": "OPENVDB", + "conditionalVisOp": "or", + "conditionalVis1Op": "equalTo", + "conditionalVisLeft": "conditionalVis1", + "conditionalVisRight": "conditionalVis2", + "conditionalVis2Path": "bl_object_type", + "conditionalVis2Value": "RI_VOLUME" + }, + "help": "Include this volume in the global volume aggregate, if it's not part of an aggregate already. Note, for XPU, volumes have to be in an aggregate in order for them to render." + }, { "panel": "OBJECT_PT_renderman_object_render", "name": "rman_visibilityCamera", diff --git a/rman_constants.py b/rman_constants.py index 0c23d685..4c854516 100644 --- a/rman_constants.py +++ b/rman_constants.py @@ -45,6 +45,7 @@ RFB_PREFS_NAME = __name__.split('.')[0] RMAN_RENDERMAN_BLUE = (0.0, 0.498, 1.0, 1.0) RMAN_FAKE_NODEGROUP = '.__RMAN_FAKE_NODEGROUP__' +RMAN_GLOBAL_VOL_AGGREGATE = 'globalVolumeAggregate' RFB_HELP_URL = "https://rmanwiki.pixar.com/display/RFB%s" % RMAN_SUPPORTED_VERSION_MAJOR diff --git a/rman_handlers/__init__.py b/rman_handlers/__init__.py index d4bcd468..27772a49 100644 --- a/rman_handlers/__init__.py +++ b/rman_handlers/__init__.py @@ -21,11 +21,13 @@ @persistent def rman_load_post(bl_scene): from ..rman_ui import rman_ui_light_handlers + from ..rfb_utils import scene_utils string_utils.update_blender_tokens_cb(bl_scene) rman_ui_light_handlers.clear_gl_tex_cache(bl_scene) texture_utils.txmanager_load_cb(bl_scene) upgrade_utils.upgrade_scene(bl_scene) + scene_utils.add_global_vol_aggregate() @persistent def rman_save_pre(bl_scene): diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py index d9d8489e..e2072f46 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py @@ -5,18 +5,29 @@ from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config from ...rfb_utils import scene_utils +from ... import rfb_icons import bpy import re class RENDERMAN_UL_Volume_Aggregates_List(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + rman_vol_agg = rfb_icons.get_icon("rman_vol_aggregates").icon_id + if index == 0: + layout.label(text=item.name, icon_value=rman_vol_agg) + else: + layout.prop(item, 'name', text='', emboss=False, icon_value=rman_vol_agg) + + +class RENDERMAN_UL_Volume_Aggregates_Objects_List(bpy.types.UIList): - custom_icon = 'OBJECT_DATAMODE' + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + + custom_icon = 'OUTLINER_OB_VOLUME' layout.context_pointer_set("selected_obj", item.ob_pointer) op = layout.operator('renderman.remove_from_vol_aggregate', text='', icon='REMOVE') label = item.ob_pointer.name - layout.label(text=label, icon=custom_icon) + layout.label(text=label, icon=custom_icon) class PRMAN_OT_Renderman_Open_Volume_Aggregates_Editor(CollectionPanel, bpy.types.Operator): @@ -87,7 +98,14 @@ def draw(self, context): "renderman.add_remove_volume_aggregates", "scene.renderman", "vol_aggregates", "vol_aggregates_index", - default_name='VolumeAggreagte_%d' % len(rm.vol_aggregates)) + default_name='VolumeAggreagte_%d' % len(rm.vol_aggregates), + ui_list_class="RENDERMAN_UL_Volume_Aggregates_List", + enable_remove_func=self.enable_remove_func) + + def enable_remove_func(self, context): + scene = context.scene + rm = scene.renderman + return (rm.vol_aggregates_index != 0) def draw_objects_item(self, layout, context, item): row = layout.row() @@ -95,6 +113,35 @@ def draw_objects_item(self, layout, context, item): rm = scene.renderman vol_aggregate = rm.vol_aggregates[rm.vol_aggregates_index] + if rm.vol_aggregates_index == 0: + # we're viewing the global volume aggregate + # just display what volumes are in the global aggregate + # and don't allow the user to edit the list + box = layout.box() + box.use_property_split = True + box.use_property_decorate = False + + # Loop over all of volume objects in the scene. + # Check if they already belong to aggregate. If they do, they + # are not the global aggregate. + for ob in scene_utils.get_all_volume_objects(scene): + if not ob.renderman.volume_global_aggregate: + # volume is should not be in the global aggregate + continue + do_draw = True + for lg in rm.vol_aggregates: + for member in lg.members: + if member.ob_pointer == ob: + do_draw = False + break + if not do_draw: + break + if do_draw: + row = box.row(align=True) + custom_icon = 'OUTLINER_OB_VOLUME' + row.label(text=ob.name, icon=custom_icon) + return + row = layout.row() row.separator() @@ -132,7 +179,7 @@ def draw_objects_item(self, layout, context, item): row = layout.row() - row.template_list('RENDERMAN_UL_Volume_Aggregates_List', "", + row.template_list('RENDERMAN_UL_Volume_Aggregates_Objects_List', "", vol_aggregate, "members", vol_aggregate, 'members_index', rows=6) def draw_item(self, layout, context, item): @@ -171,7 +218,8 @@ def invoke(self, context, event): classes = [ PRMAN_OT_Renderman_Open_Volume_Aggregates_Editor, - RENDERMAN_UL_Volume_Aggregates_List + RENDERMAN_UL_Volume_Aggregates_List, + RENDERMAN_UL_Volume_Aggregates_Objects_List ] def register(): diff --git a/rman_operators/rman_operators_volumes.py b/rman_operators/rman_operators_volumes.py index 8d40ea3d..c5d9a3bc 100644 --- a/rman_operators/rman_operators_volumes.py +++ b/rman_operators/rman_operators_volumes.py @@ -86,6 +86,8 @@ def add_selected(self, context): scene = context.scene rm = scene.renderman vol_aggregates_index = rm.vol_aggregates_index + if vol_aggregates_index == 0: + return {'FINISHED'} ob = getattr(context, "selected_obj", None) if not ob: return {'FINISHED'} @@ -157,7 +159,7 @@ def execute(self, context): for i, member in enumerate(vol_aggregate.members): if member.ob_pointer == ob: vol_aggregate.members.remove(i) - ob.update_tag(refresh={'OBJECT'}) + ob.update_tag(refresh={'DATA'}) break return {'FINISHED'} diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index 07ea2125..2c3e057c 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -355,6 +355,8 @@ def draw(self, context): layout.separator() layout.label(text='Add Selected To: ') for i, v in enumerate(vol_aggregates): + if i == 0: + continue op = layout.operator('renderman.add_to_vol_aggregate', text=v.name) op.vol_aggregates_index = i op.do_scene_selected = True From 0290229d3fcabd733eddd64f20e90e499fed4565 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 24 Mar 2023 16:40:09 -0700 Subject: [PATCH 253/278] Use the new frame range format for the denoiser tasks, but only if by == 1. --- rfb_utils/string_expr.py | 38 +++++++++++++++++-------- rfb_utils/string_utils.py | 4 +-- rman_spool.py | 59 +++++++++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/rfb_utils/string_expr.py b/rfb_utils/string_expr.py index 7e69f139..50ca7bd4 100644 --- a/rfb_utils/string_expr.py +++ b/rfb_utils/string_expr.py @@ -145,18 +145,32 @@ def update_blend_tokens(self): # @time_this def set_frame_context(self, frame): """Set the scene frame for the next subst() call.""" - self.tokens['frame'] = str(frame) - iframe = int(frame) - self.tokens['f'] = str(iframe) - self.tokens['f2'] = '{:0>2d}'.format(iframe) - self.tokens['f3'] = '{:0>3d}'.format(iframe) - self.tokens['f4'] = '{:0>4d}'.format(iframe) - self.tokens['f5'] = '{:0>5d}'.format(iframe) - self.tokens['F'] = str(iframe) - self.tokens['F2'] = '{:0>2d}'.format(iframe) - self.tokens['F3'] = '{:0>3d}'.format(iframe) - self.tokens['F4'] = '{:0>4d}'.format(iframe) - self.tokens['F5'] = '{:0>5d}'.format(iframe) + + if isinstance(frame, int): + self.tokens['frame'] = str(frame) + iframe = int(frame) + self.tokens['f'] = str(iframe) + self.tokens['f2'] = '{:0>2d}'.format(iframe) + self.tokens['f3'] = '{:0>3d}'.format(iframe) + self.tokens['f4'] = '{:0>4d}'.format(iframe) + self.tokens['f5'] = '{:0>5d}'.format(iframe) + self.tokens['F'] = str(iframe) + self.tokens['F2'] = '{:0>2d}'.format(iframe) + self.tokens['F3'] = '{:0>3d}'.format(iframe) + self.tokens['F4'] = '{:0>4d}'.format(iframe) + self.tokens['F5'] = '{:0>5d}'.format(iframe) + elif isinstance(frame, str): + self.tokens['frame'] = frame + self.tokens['f'] = frame + self.tokens['f2'] = '%s' % (frame) * 2 + self.tokens['f3'] = '%s' % (frame) * 3 + self.tokens['f4'] = '%s' % (frame) * 4 + self.tokens['f5'] = '%s' % (frame) * 5 + self.tokens['F'] = frame + self.tokens['F2'] = '%s' % (frame) * 2 + self.tokens['F3'] = '%s' % (frame) * 3 + self.tokens['F4'] = '%s' % (frame) * 4 + self.tokens['F5'] = '%s' % (frame) * 5 # @time_this def expand(self, expr, objTokens={}, asFilePath=False): diff --git a/rfb_utils/string_utils.py b/rfb_utils/string_utils.py index 48e144ed..06f1266c 100644 --- a/rfb_utils/string_utils.py +++ b/rfb_utils/string_utils.py @@ -34,7 +34,7 @@ def expand(self, string, display=None, frame=None, token_dict = dict(), asFilePa Kwargs: - display (str): The display being considered. This is necessary if your expression contains or - - frame (int): An optional frame number to expand , , etc. + - frame (int, str): An optional frame number to expand , , etc. Returns: - The expanded string @@ -110,7 +110,7 @@ def expand_string(string, display=None, glob_sequence=False, frame=None, token_d Kwargs: - display (str): the name of a display driver to update tokens. - - frame (str): the frame to use for expanding + - frame (int, str): the frame to use for expanding. If a string, the string will be repeated by the paddning. Ex: '#' will turn to '####' for - token_dict (dict): dictionary of token/vals that also need to be set. - asFilePath (bool): treat the input string as a path. Will create directories if they don't exist diff --git a/rman_spool.py b/rman_spool.py index c9fd8559..a45e5b25 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -247,41 +247,70 @@ def generate_aidenoise_tasks(self, start, last, by): img_files = list() preview_img_files = list() have_variance = False - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], + beauty_path = dspys_dict['displays']['beauty']['filePath'] + variance_file = string_utils.expand_string(beauty_path, frame=1, asFilePath=True) path = filepath_utils.get_real_path(rm.ai_denoiser_output_dir) if not os.path.exists(path): - path = os.path.join(os.path.dirname(variance_file), 'denoised') - for frame_num in range(start, last + 1, by): - self.rman_render.bl_frame_current = frame_num - - variance_file = string_utils.expand_string(dspys_dict['displays']['beauty']['filePath'], - frame=frame_num, - asFilePath=True) + path = os.path.join(os.path.dirname(variance_file), 'denoised') + do_cross_frame = (rm.ai_denoiser_mode == 'crossframe') + if by > 1: + do_cross_frame = False # can't do crossframe if by > 1 + for frame_num in range(start, last + 1, by): + self.rman_render.bl_frame_current = frame_num + + variance_file = string_utils.expand_string(beauty_path, + frame=frame_num, + asFilePath=True) + for dspy,params in dspys_dict['displays'].items(): + if not params['denoise']: + continue + if dspy == 'beauty': + if not have_variance: + have_variance = True + img_files.append(variance_file) + preview_img_files.append(os.path.join(path, os.path.basename(variance_file))) + else: + token_dict = {'aov': dspy} + aov_file = string_utils.expand_string(params['filePath'], + frame=frame_num, + token_dict=token_dict, + asFilePath=True) + img_files.append(aov_file) + else: + # use frame range format + # ex: foo.####.exr start-last + for frame_num in range(start, last + 1): + variance_file = string_utils.expand_string(beauty_path, + frame=frame_num, + asFilePath=True) + preview_img_files.append(os.path.join(path, os.path.basename(variance_file))) + for dspy,params in dspys_dict['displays'].items(): if not params['denoise']: continue if dspy == 'beauty': if not have_variance: have_variance = True + variance_file = string_utils.expand_string(beauty_path, + frame='#', + asFilePath=True) img_files.append(variance_file) - preview_img_files.append(os.path.join(path, os.path.basename(variance_file))) else: token_dict = {'aov': dspy} aov_file = string_utils.expand_string(params['filePath'], - frame=frame_num, token_dict=token_dict, + frame="#", asFilePath=True) - img_files.append(aov_file) + img_files.append(aov_file) + + img_files.append('%d-%d' % (start, last)) if not have_variance or len(img_files) < 1: return None - - do_cross_frame = (rm.ai_denoiser_mode == 'crossframe') - - + if start == last: do_cross_frame = False From 9811d77d49f5bc3302b0b54a3a932624f64a5526 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 27 Mar 2023 16:12:20 -0700 Subject: [PATCH 254/278] Fix bug where we were not showing the txmanager button when UI framework was set to Qt. --- rman_ui/rman_ui_txmanager.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index 70e16e75..bd2828d1 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -723,13 +723,8 @@ def draw_txmanager_layout(cls, context, layout): def draw(self, context): layout = self.layout if get_pref('rman_ui_framework') == 'QT': - try: - from . import rman_ui_txmanager_qt - if rman_ui_txmanager_qt.__QT_LOADED__: - rman_icon = rfb_icons.get_icon('rman_txmanager') - layout.operator("rman_txmgr_list.open_txmanager", icon_value=rman_icon.icon_id) - except: - PRMAN_PT_Renderman_txmanager_list.draw_txmanager_layout(context, layout) + rman_icon = rfb_icons.get_icon('rman_txmanager') + layout.operator("rman_txmgr_list.open_txmanager", icon_value=rman_icon.icon_id) else: PRMAN_PT_Renderman_txmanager_list.draw_txmanager_layout(context, layout) From 065ee04e356145cc369b84e8de8c9cca3396690b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 27 Mar 2023 16:29:54 -0700 Subject: [PATCH 255/278] Hide OK and cancel buttons for the Qt version of trace sets editorand light linker. --- rfb_utils/property_callbacks.py | 4 ++++ .../rman_operators_editors_lightlink.py | 5 ++++- .../rman_operators_editors_tracegroups.py | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/rfb_utils/property_callbacks.py b/rfb_utils/property_callbacks.py index 4aff3fd8..62918510 100644 --- a/rfb_utils/property_callbacks.py +++ b/rfb_utils/property_callbacks.py @@ -219,10 +219,14 @@ def update_riattr_func(self, s, context): ob = None if not hasattr(context, 'object'): ob = self.id_data + if ob is None: + return scenegraph_utils.update_sg_node_riattr(s, context, bl_object=ob) def update_primvar_func(self, s, context): ob = None if not hasattr(context, 'object'): ob = self.id_data + if ob is None: + return scenegraph_utils.update_sg_node_primvar(s, context, bl_object=ob) \ No newline at end of file diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 5e09c6db..9ccea720 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -47,7 +47,10 @@ def __init__(self) -> None: self.resize(825, 526) self.buttonBox = QtWidgets.QDialogButtonBox(self) self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + # hide OK, and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") #self.invert_checkBox = QtWidgets.QCheckBox(self) #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 56b5c989..33da2e22 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -41,7 +41,10 @@ def __init__(self) -> None: self.buttonBox = QtWidgets.QDialogButtonBox(self) self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") self.addButton = QtWidgets.QPushButton(self) self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) From 7ac26ac1303ec3ea179bff50757abee458f31555 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 29 Mar 2023 12:29:44 -0700 Subject: [PATCH 256/278] Fix for RMAN-20494 From some reason, on Windows and linux, we're not inheriting the CSS correctly from the child widgets. For now, have rfb_qt.py explicitly set the the style sheet needed. --- rman_presets/ui.py | 11 +-- rman_ui/rfb_qt.py | 165 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 10 deletions(-) diff --git a/rman_presets/ui.py b/rman_presets/ui.py index 2023e8c8..963cc184 100644 --- a/rman_presets/ui.py +++ b/rman_presets/ui.py @@ -73,18 +73,9 @@ def __init__(self): self.resize(1024, 1024) self.setWindowTitle('RenderMan Preset Browser') - if sys.platform != "darwin": - bg_role = self.backgroundRole() - plt = self.palette() - bg_color = plt.color(bg_role) - bg_color.setRgb(70, 70, 70) - plt.setColor(bg_role, bg_color) - self.setPalette(plt) - self.hostPrefs = bl_pb_core.get_host_prefs() self.ui = rui.Ui(self.hostPrefs, parent=self) - self.setLayout(self.ui.topLayout) - self.show() # Show window + self.setLayout(self.ui.topLayout) def closeEvent(self, event): self.hostPrefs.saveAllPrefs() diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index 48f4f103..84056a85 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -47,6 +47,149 @@ """ +## CSS copied from $RMANTREE/bin/rman_utils/rman_assets/common/ui_style.py +__rmanPltF__ = {'bg': (68, 68, 68), + 'darkbg': (43, 43, 43), + 'lightbg': (78, 78, 78), + 'tipbg': (58, 58, 58), + 'tiptext': (192, 192, 192), + 'text': (200, 200, 200), + 'textselected': (225, 225, 225), + 'orange': (229, 154, 0), + 'blue': (118, 149, 229), + 'bluehover': (81, 95, 125), + 'handle': (93, 93, 93)} + +__BASE_CSS__ = ''' + QWidget { + background: %(bg)s; + } + QPushButton { + border-radius: 2px; + color: %(text)s; + background-color: #5D5D5D; + min-height: 18px; + margin-left: 5px; + margin-right: 5px; + margin-top: 1px; + padding-left: 3px; + padding-right: 3px; + } + QPushButton:hover { + background-color: #5D5D5D; + color: %(textselected)s; + } + QPushButton:pressed { + background-color: rgba(32, 64, 128, 255); + color: %(textselected)s; + } + QFrame { + background-color: %(darkbg)s; + border-width: 2px; + border-radius: 4px; + margin: 0px; + } + QLabel { + background: %(bg)s; + color: %(text)s; + } + QGroupBox { + background: %(bg)s; + color: %(text)s; + } + QSplitter { + border-style: none; + background-color: %(bg)s; + } + QSplitter::handle { + background-color: %(bg)s; + } + QSplitter::handle:hover { + background-color: %(bluehover)s; + } + QMenuBar { + border-width: 0px; + border-image: none; + color: %(text)s; + } + QMenuBar::item { + color: %(text)s; + background-color: %(bg)s; + } + QMenuBar::item::selected { + background-color: %(bg)s; + color: %(textselected)s; + } + QMenu { + background-color: %(bg)s; + color: %(text)s; + } + QMenu::item::selected { + background-color: %(bg)s; + color: %(textselected)s; + } + QToolTip { + background-color: %(tipbg)s; + color: %(tiptext)s; + border: 3px solid %(bluehover)s; + border-radius: 3px; + padding: 4px; + } + QProgressBar { + border: 1px solid %(bg)s; + } + QProgressBar::chunk { + background-color: %(blue)s; + } + QLineEdit { + background-color: %(darkbg)s; + background-image: none; + color: %(text)s; + } + QHeaderView { + background-color: %(darkbg)s; + border-color: %(darkbg)s; + } + QHeaderView::section { + background-color: %(darkbg)s; + background-image: none; + border-image: none; + border-color: %(darkbg)s; + color: %(blue)s; + font-weight: bold; + } + QTreeWidget { + margin: 0px; + padding: 0px; + border-width: 2px; + border-radius: 4px; + border-color: %(darkbg)s; + color: %(text)s; + background-color: %(darkbg)s; + min-width: 138px; + } + QTreeView { + margin: 0px; + padding: 0px; + border-width: 2px; + border-radius: 4px; + border-color: %(darkbg)s; + color: %(text)s; + background-color: %(darkbg)s; + min-width: 138px; + } + QListWidget { + margin: 0px; + padding: 0px; + border-width: 2px; + border-radius: 4px; + border-color: %(darkbg)s; + color: %(text)s; + background-color: %(darkbg)s; + min-width: 138px; + } +''' + class RfbBaseQtAppTimed(bpy.types.Operator): """Run a Qt app inside of Blender, without blocking Blender.""" @@ -57,6 +200,9 @@ class RfbBaseQtAppTimed(bpy.types.Operator): def __init__(self): self._app = (QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)) + + # always use the Fusion style + self._app.setStyle("Fusion") def modal(self, context, event): """Run modal.""" @@ -70,6 +216,18 @@ def modal(self, context, event): def execute(self, context): """Process the event loop of the Qt app.""" + + # explicitly set the style sheet + # we don't seem to be inheriting the style sheet correctly + # from the children widgets + sh = self._window.styleSheet() + plt = dict(__rmanPltF__) + for nm, rgb in plt.items(): + plt[nm] = 'rgb(%d, %d, %d)' % (rgb[0], rgb[1], rgb[2]) + css = __BASE_CSS__ % plt + sh += css + self._app.setStyleSheet(sh) + self._window.show() wm = context.window_manager # Run every 0.01 seconds @@ -93,6 +251,13 @@ def __init__(self): else: self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) + bg_role = self.backgroundRole() + plt = self.palette() + bg_color = plt.color(bg_role) + bg_color.setRgb(__rmanPltF__['bg'][0], __rmanPltF__['bg'][1], __rmanPltF__['bg'][2]) + plt.setColor(bg_role, bg_color) + self.setPalette(plt) + def closeEvent(self, event): event.accept() From 3dfaa05cb8d0eeff92139deb3b7fc03c8defc506 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 29 Mar 2023 13:00:59 -0700 Subject: [PATCH 257/278] Another CSS fix. Need an alternate-background-color for QTreeView --- rman_ui/rfb_qt.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index 84056a85..baab9de8 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -50,6 +50,7 @@ ## CSS copied from $RMANTREE/bin/rman_utils/rman_assets/common/ui_style.py __rmanPltF__ = {'bg': (68, 68, 68), 'darkbg': (43, 43, 43), + 'alternatebg': (53, 53, 53), 'lightbg': (78, 78, 78), 'tipbg': (58, 58, 58), 'tiptext': (192, 192, 192), @@ -166,6 +167,7 @@ border-color: %(darkbg)s; color: %(text)s; background-color: %(darkbg)s; + alternate-background-color: %(alternatebg)s; min-width: 138px; } QTreeView { @@ -176,6 +178,7 @@ border-color: %(darkbg)s; color: %(text)s; background-color: %(darkbg)s; + alternate-background-color: %(alternatebg)s; min-width: 138px; } QListWidget { @@ -186,6 +189,7 @@ border-color: %(darkbg)s; color: %(text)s; background-color: %(darkbg)s; + alternate-background-color: %(alternatebg)s; min-width: 138px; } ''' From 1c91d9b4990301d18db77a96e95839818fce68cd Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 30 Mar 2023 09:05:35 -0700 Subject: [PATCH 258/278] Fix a couple of more bugs with the Qt versions of the light linking editor and trace sets editor. Add an option to show these WIP Qt editors (default to off) in the preferences. --- preferences.py | 8 ++++ rfb_utils/prefs_utils.py | 3 ++ .../rman_operators_editors_lightlink.py | 8 ++-- .../rman_operators_editors_tracegroups.py | 48 +++++++++++++------ rman_ui/rman_ui_view3d_menus.py | 8 +++- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/preferences.py b/preferences.py index 22b0efa1..3a76bc35 100644 --- a/preferences.py +++ b/preferences.py @@ -471,6 +471,12 @@ def update_rman_logging_level(self, context): ] ) + rman_show_wip_qt: BoolProperty( + name="Show WIP UI", + default=False, + description="Show WIP Qt UI. Not all of our UI have been completely converted to Qt. Turn this option off to go back to the native version, even if UI Framework is set to Qt." + ) + # For the preset browser rpbConfigFile: StringProperty(default='') rpbUserLibraries: CollectionProperty(type=RendermanPreferencePath) @@ -656,6 +662,8 @@ def draw(self, context): col.prop(self, 'rman_viewport_progress_color') col.prop(self, 'draw_panel_icon') col.prop(self, 'rman_ui_framework') + if self.rman_ui_framework == 'QT': + col.prop(self, 'rman_show_wip_qt') # Logging row = layout.row() diff --git a/rfb_utils/prefs_utils.py b/rfb_utils/prefs_utils.py index 201fd81c..1b2a6df3 100644 --- a/rfb_utils/prefs_utils.py +++ b/rfb_utils/prefs_utils.py @@ -15,6 +15,9 @@ def get_addon_prefs(): def using_qt(): return get_pref('rman_ui_framework') == 'QT' +def show_wip_qt(): + return get_pref('rman_show_wip_qt') + def get_pref(pref_name='', default=None): """ Return the value of a preference diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 9ccea720..ba529bc4 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -5,7 +5,7 @@ from ...rfb_utils import string_utils from ...rfb_utils import scenegraph_utils from ...rfb_logger import rfb_log -from ...rfb_utils.prefs_utils import get_pref, using_qt +from ...rfb_utils.prefs_utils import get_pref, using_qt, show_wip_qt from ...rfb_utils import object_utils from ...rfb_utils.envconfig_utils import envconfig from ... import rfb_icons @@ -128,8 +128,8 @@ def depsgraph_update_post(self, bl_scene, depsgraph): self.refresh_lights() self.refresh_linked_objects() self.lights_index_changed() - elif isinstance(dps_update.id, bpy.types.Scene): - self.refresh_lights() + #elif isinstance(dps_update.id, bpy.types.Scene): + # self.refresh_lights() def update(self): super(LightLinkingQtWrapper, self).update() @@ -656,7 +656,7 @@ def check_light_links(self, context): def invoke(self, context, event): - if using_qt() and envconfig().getenv('RFB_DEVELOPER'): + if using_qt() and show_wip_qt(): global __LIGHT_LINKING_WINDOW__ if sys.platform == "darwin": rfb_qt.run_with_timer(__LIGHT_LINKING_WINDOW__, LightLinkingQtWrapper) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 33da2e22..3d2c9e3a 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -5,7 +5,7 @@ from ...rfb_logger import rfb_log from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config -from ...rfb_utils.prefs_utils import get_pref, using_qt +from ...rfb_utils.prefs_utils import get_pref, using_qt, show_wip_qt from ...rman_ui import rfb_qt as rfb_qt from ...rfb_utils.envconfig_utils import envconfig import bpy @@ -98,6 +98,7 @@ def __init__(self) -> None: self.refresh_groups() self.refresh_group_objects() + self.checkTraceGroups() self.traceGroupObjects.expandAll() @@ -117,16 +118,14 @@ def remove_handlers(self): def depsgraph_update_post(self, bl_scene, depsgraph): for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Scene): - self.trace_groups_index_changed() - elif isinstance(dps_update.id, bpy.types.Object) or isinstance(dps_update.id, bpy.types.Collection): - self.refresh_groups() - self.refresh_group_objects() - - - def update(self): - idx = int(self.traceGroups.currentRow()) - self.addButton.setEnabled(True) + if isinstance(dps_update.id, bpy.types.Collection): + #self.refresh_groups() + self.traceGroups.setCurrentRow(-1) + self.refresh_group_objects() + #elif isinstance(dps_update.id, bpy.types.Scene): + # self.trace_groups_index_changed() + + def checkTraceGroups(self): if self.traceGroups.count() < 1: self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.enable_trace_group_objects(self.rootNode, enable=False) @@ -134,8 +133,13 @@ def update(self): else: self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) self.removeButton.setEnabled(True) - self.enable_trace_group_objects(self.rootNode, enable=True) + self.enable_trace_group_objects(self.rootNode, enable=True) + def update(self): + idx = int(self.traceGroups.currentRow()) + self.addButton.setEnabled(True) + + self.checkTraceGroups() super(TraceGroupsQtWrapper, self).update() def refresh_groups(self): @@ -255,6 +259,7 @@ def add_children(root_item, ob): def trace_groups_index_changed(self): idx = int(self.traceGroups.currentRow()) current_item = self.traceGroups.currentItem() + self.checkTraceGroups() if current_item: self.label_2.setText("Objects (%s)" % current_item.text()) else: @@ -281,6 +286,11 @@ def trace_groups_index_changed(self): self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) def trace_group_objects_selection(self, selected, deselected): + idx = int(self.traceGroups.currentRow()) + current_item = self.traceGroups.currentItem() + if not current_item: + return + context = bpy.context scene = context.scene rm = scene.renderman @@ -291,6 +301,17 @@ def trace_group_objects_selection(self, selected, deselected): return object_group = object_groups[group_index] + for i in deselected.indexes(): + item = self.traceGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + for i, member in enumerate(object_group.members): + if ob == member.ob_pointer: + object_group.members.remove(i) + ob.update_tag(refresh={'OBJECT'}) + break + for i in selected.indexes(): item = self.traceGroupObjects.model().itemFromIndex(i) ob = bpy.data.objects.get(item.text(), None) @@ -307,7 +328,6 @@ def trace_group_objects_selection(self, selected, deselected): ob_in_group.ob_pointer = ob ob.update_tag(refresh={'OBJECT'}) - class RENDERMAN_UL_Object_Group_List(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -461,7 +481,7 @@ def check_tracegroups(self, context): def invoke(self, context, event): - if using_qt() and envconfig().getenv('RFB_DEVELOPER'): + if using_qt() and show_wip_qt(): global __TRACE_GROUPS_WINDOW__ if sys.platform == "darwin": rfb_qt.run_with_timer(__TRACE_GROUPS_WINDOW__, TraceGroupsQtWrapper) diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index 2c3e057c..e9785220 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -5,7 +5,7 @@ from ..rfb_utils import shadergraph_utils from ..rfb_utils import object_utils from ..rfb_utils.envconfig_utils import envconfig -from ..rfb_utils.prefs_utils import using_qt +from ..rfb_utils.prefs_utils import using_qt, show_wip_qt from ..rfb_logger import rfb_log from ..rman_config import __RFB_CONFIG_DICT__ as rfb_config from bpy.types import Menu @@ -269,8 +269,10 @@ def draw(self, context): return if light_props.renderman_light_role not in {'RMAN_LIGHTFILTER', 'RMAN_LIGHT'}: return + if using_qt() and show_wip_qt(): + return selected_objects = context.selected_objects - if not using_qt() and not envconfig().getenv('RFB_DEVELOPER') and selected_objects: + if selected_objects: layout.context_pointer_set('light_ob', active_light) if not rm.invert_light_linking: layout.separator() @@ -475,6 +477,8 @@ def draw(self, context): scene = context.scene op = layout.operator("scene.rman_open_groups_editor", text="Trace Sets Editor") + if using_qt() and show_wip_qt(): + return selected_objects = [] if context.selected_objects: for obj in context.selected_objects: From cde599e04cfbc032de9816943d45437183a669a3 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 30 Mar 2023 10:09:35 -0700 Subject: [PATCH 259/278] When opening editors in Qt mode, we should return "FINISHED", instead of "RUNNING_MODAL". The latter seems to be creating memory leaks. --- .../rman_operators_editors/rman_operators_editors_lightlink.py | 2 +- .../rman_operators_editors_tracegroups.py | 2 +- rman_presets/ui.py | 2 +- rman_stats/operators.py | 2 +- rman_ui/rman_ui_txmanager.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index ba529bc4..72a433b2 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -663,7 +663,7 @@ def invoke(self, context, event): else: bpy.ops.wm.light_linking_qt_app_timed() - return {'RUNNING_MODAL'} + return {'FINISHED'} wm = context.window_manager width = rfb_config['editor_preferences']['lightlink_editor']['width'] diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 3d2c9e3a..a8233e26 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -488,7 +488,7 @@ def invoke(self, context, event): else: bpy.ops.wm.trace_groups_qt_app_timed() - return {'RUNNING_MODAL'} + return {'FINISHED'} wm = context.window_manager width = rfb_config['editor_preferences']['tracesets_editor']['width'] diff --git a/rman_presets/ui.py b/rman_presets/ui.py index 963cc184..e8a09a94 100644 --- a/rman_presets/ui.py +++ b/rman_presets/ui.py @@ -496,7 +496,7 @@ def invoke(self, context, event): else: bpy.ops.wm.rpb_qt_app_timed() - return {'RUNNING_MODAL'} + return {'FINISHED'} self.load_categories(context) self.load_presets(context) diff --git a/rman_stats/operators.py b/rman_stats/operators.py index ef07aad1..a5e8e341 100644 --- a/rman_stats/operators.py +++ b/rman_stats/operators.py @@ -68,7 +68,7 @@ def execute(self, context): else: bpy.ops.wm.live_stats_qt_app_timed() - return {'RUNNING_MODAL'} + return {'FINISHED'} classes = [ PRMAN_OT_Open_Stats, diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index bd2828d1..70a43e0c 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -765,7 +765,7 @@ def invoke(self, context, event): txfile = mgr.get_txfile_from_id(self.nodeID) mgr.ui.select_txfile(txfile) - return {'RUNNING_MODAL'} + return {'FINISHED'} if self.properties.nodeID != '': From 35bc9b6c78dbaa1ce58cca41577c7368adcb6e44 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 30 Mar 2023 13:34:17 -0700 Subject: [PATCH 260/278] Add a simple Qt version of the volume aggregates editor --- .../rman_operators_editors_tracegroups.py | 28 +- .../rman_operators_editors_vol_aggregates.py | 327 +++++++++++++++++- 2 files changed, 347 insertions(+), 8 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index a8233e26..3c70e850 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -1,13 +1,10 @@ -from logging import root from bpy.props import (StringProperty, BoolProperty, EnumProperty) from ...rman_ui.rman_ui_base import CollectionPanel -from ...rfb_logger import rfb_log from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config -from ...rfb_utils.prefs_utils import get_pref, using_qt, show_wip_qt +from ...rfb_utils.prefs_utils import using_qt, show_wip_qt from ...rman_ui import rfb_qt as rfb_qt -from ...rfb_utils.envconfig_utils import envconfig import bpy import re import sys @@ -192,6 +189,9 @@ def trace_group_changed(self, item): grp = rm.object_groups[idx] grp.name = item.text() self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.ob_pointer + ob.update_tag(refresh={'OBJECT'}) def find_item(self, standard_item, ob): ''' @@ -235,6 +235,8 @@ def refresh_group_objects(self): def add_children(root_item, ob): for child in ob.children: + if child.type in ['CAMERA', 'ARMATURE']: + continue item = self.find_item(root_item, child) if not item: item = StandardItem(txt=child.name) @@ -244,6 +246,8 @@ def add_children(root_item, ob): root_parents = [ob for ob in scene.objects if ob.parent is None] for ob in root_parents: + if ob.type in ('ARMATURE', 'CAMERA'): + continue item = self.find_item(self.rootNode, ob) if not item: @@ -256,6 +260,14 @@ def add_children(root_item, ob): if idx != -1: self.trace_groups_index_changed() + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + def trace_groups_index_changed(self): idx = int(self.traceGroups.currentRow()) current_item = self.traceGroups.currentItem() @@ -274,6 +286,7 @@ def trace_groups_index_changed(self): object_group = object_groups[group_index] selected_items = QtCore.QItemSelection() + obs = [] for member in object_group.members: ob = member.ob_pointer if ob is None: @@ -283,7 +296,9 @@ def trace_groups_index_changed(self): idx = self.treeModel.indexFromItem(item) selection_range = QtCore.QItemSelectionRange(idx) selected_items.append(selection_range) + obs.append(ob) self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + self.bl_select_objects(obs) def trace_group_objects_selection(self, selected, deselected): idx = int(self.traceGroups.currentRow()) @@ -312,6 +327,7 @@ def trace_group_objects_selection(self, selected, deselected): ob.update_tag(refresh={'OBJECT'}) break + obs = [] for i in selected.indexes(): item = self.traceGroupObjects.model().itemFromIndex(i) ob = bpy.data.objects.get(item.text(), None) @@ -321,12 +337,14 @@ def trace_group_objects_selection(self, selected, deselected): for member in object_group.members: if ob == member.ob_pointer: do_add = False - break + obs.append(member.ob_pointer) if do_add: + obs.append(ob) ob_in_group = object_group.members.add() ob_in_group.name = ob.name ob_in_group.ob_pointer = ob ob.update_tag(refresh={'OBJECT'}) + self.bl_select_objects(obs) class RENDERMAN_UL_Object_Group_List(bpy.types.UIList): diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py index e2072f46..e532794a 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py @@ -1,13 +1,325 @@ from bpy.props import (StringProperty, BoolProperty, EnumProperty) from ...rman_ui.rman_ui_base import CollectionPanel -from ...rfb_logger import rfb_log from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config from ...rfb_utils import scene_utils from ... import rfb_icons +from ...rfb_utils.prefs_utils import using_qt, show_wip_qt +from ...rman_ui import rfb_qt as rfb_qt import bpy import re +import sys +from PySide2 import QtCore, QtWidgets, QtGui + +__VOL_AGGREGATE_WINDOW__ = None + +class VolAggregateQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.vol_aggregates_qt_app_timed" + bl_label = "RenderMan Volume Aggregates Editor" + + def __init__(self): + super(VolAggregateQtAppTimed, self).__init__() + + def execute(self, context): + self._window = VolAggregatesQtWrapper() + return super(VolAggregateQtAppTimed, self).execute(context) + +class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + +class VolAggregatesQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(VolAggregatesQtWrapper, self).__init__() + + self.setWindowTitle('RenderMan Volume Aggregates') + self.resize(620, 475) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + + self.volAggregateGroupObjects = QtWidgets.QTreeView(self) + self.volAggregateGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.volAggregateGroupObjects.setModel(self.treeModel) + + self.volAggregateGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) + self.volAggregateGroupObjects.setObjectName("volAggregateGroupObjects") + self.volAggregateGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + + self.volAggregateGroups = QtWidgets.QListWidget(self) + self.volAggregateGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.volAggregateGroups.setObjectName("volAggregateGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) + self.label.setText("Volume Aggregates") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Objects") + + self.refresh_btn = QtWidgets.QPushButton(self) + self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) + self.refresh_btn.setText("Refresh") + self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + QtCore.QMetaObject.connectSlotsByName(self) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.refresh_btn.clicked.connect(self.refresh_group_objects) + + self.volAggregateGroups.itemChanged.connect(self.vol_aggregate_group_changed) + self.volAggregateGroups.itemSelectionChanged.connect(self.vol_aggregate_groups_index_changed) + self.volAggregateGroupObjects.selectionModel().selectionChanged.connect(self.vol_aggregate_group_objects_selection) + + self.refresh_groups() + self.refresh_group_objects() + self.checkvolAggregateGroups() + + self.volAggregateGroupObjects.expandAll() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(VolAggregatesQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.volAggregateGroups.setCurrentRow(-1) + self.refresh_group_objects() + + def checkvolAggregateGroups(self): + if self.volAggregateGroups.count() < 1: + self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.vol_aggregate_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.removeButton.setEnabled(True) + self.vol_aggregate_group_objects(self.rootNode, enable=True) + + def update(self): + idx = int(self.volAggregateGroups.currentRow()) + self.addButton.setEnabled(True) + + self.checkvolAggregateGroups() + super(VolAggregatesQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.volAggregateGroups.clear() + for i, grp in enumerate(rm.vol_aggregates): + if i == 0: + continue + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.volAggregateGroups.addItem(item) + + if self.volAggregateGroups.count() > 0: + self.volAggregateGroups.setCurrentRow(rm.vol_aggregates_index) + + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.vol_aggregates.add() + grp.name = 'VolumeAggreagte_%d' % (len(rm.vol_aggregates)-2) + rm.vol_aggregates_index = len(rm.vol_aggregates)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.vol_aggregates_index + group = rm.vol_aggregates[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.vol_aggregates.remove(index) + rm.vol_aggregates_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'DATA'}) + + self.refresh_groups() + + def vol_aggregate_group_changed(self, item): + idx = int(self.volAggregateGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.vol_aggregates[idx+1] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.ob_pointer + ob.update_tag(refresh={'DATA'}) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + + return None + + def vol_aggregate_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.vol_aggregate_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.volAggregateGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + context = bpy.context + scene = context.scene + rm = scene.renderman + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + root_parents = scene_utils.get_all_volume_objects(scene) + for ob in root_parents: + + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + + self.volAggregateGroupObjects.expandAll() + if idx != -1: + self.vol_aggregate_groups_index_changed() + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + + def vol_aggregate_groups_index_changed(self): + idx = int(self.volAggregateGroups.currentRow()) + current_item = self.volAggregateGroups.currentItem() + self.checkvolAggregateGroups() + if current_item: + self.label_2.setText("Objects (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.vol_aggregates_index = idx + 1 + + group_index = rm.vol_aggregates_index + vol_aggregates = rm.vol_aggregates + object_group = vol_aggregates[group_index] + + selected_items = QtCore.QItemSelection() + obs = [] + for member in object_group.members: + ob = member.ob_pointer + if ob is None: + continue + item = self.find_item(self.rootNode, ob) + if item: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + obs.append(ob) + self.volAggregateGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + self.bl_select_objects(obs) + + def vol_aggregate_group_objects_selection(self, selected, deselected): + idx = int(self.volAggregateGroups.currentRow()) + current_item = self.volAggregateGroups.currentItem() + if not current_item: + return + + context = bpy.context + scene = context.scene + rm = scene.renderman + + group_index = rm.vol_aggregates_index + vol_aggregates = rm.vol_aggregates + if group_index not in range(0, len(vol_aggregates)): + return + object_group = vol_aggregates[group_index] + + for i in deselected.indexes(): + item = self.volAggregateGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + for i, member in enumerate(object_group.members): + if ob == member.ob_pointer: + object_group.members.remove(i) + ob.update_tag(refresh={'DATA'}) + break + + obs = [] + for i in selected.indexes(): + item = self.volAggregateGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in object_group.members: + if ob == member.ob_pointer: + do_add = False + obs.append(member.ob_pointer) + if do_add: + obs.append(ob) + ob_in_group = object_group.members.add() + ob_in_group.name = ob.name + ob_in_group.ob_pointer = ob + ob.update_tag(refresh={'DATA'}) + self.bl_select_objects(obs) class RENDERMAN_UL_Volume_Aggregates_List(bpy.types.UIList): @@ -98,7 +410,7 @@ def draw(self, context): "renderman.add_remove_volume_aggregates", "scene.renderman", "vol_aggregates", "vol_aggregates_index", - default_name='VolumeAggreagte_%d' % len(rm.vol_aggregates), + default_name='VolumeAggreagte_%d' % (len(rm.vol_aggregates)-2), ui_list_class="RENDERMAN_UL_Volume_Aggregates_List", enable_remove_func=self.enable_remove_func) @@ -209,6 +521,14 @@ def check_aggregates(self, context): lg.members_index -= 1 def invoke(self, context, event): + if using_qt() and show_wip_qt(): + global __VOL_AGGREGATE_WINDOW__ + if sys.platform == "darwin": + rfb_qt.run_with_timer(__VOL_AGGREGATE_WINDOW__, VolAggregatesQtWrapper) + else: + bpy.ops.wm.vol_aggregates_qt_app_timed() + + return {'FINISHED'} wm = context.window_manager width = rfb_config['editor_preferences']['vol_aggregates_editor']['width'] @@ -219,7 +539,8 @@ def invoke(self, context, event): classes = [ PRMAN_OT_Renderman_Open_Volume_Aggregates_Editor, RENDERMAN_UL_Volume_Aggregates_List, - RENDERMAN_UL_Volume_Aggregates_Objects_List + RENDERMAN_UL_Volume_Aggregates_Objects_List, + VolAggregateQtAppTimed ] def register(): From 2d46d30f16d8ad86b9e785b19f5bc48b75421812 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 30 Mar 2023 13:38:42 -0700 Subject: [PATCH 261/278] Don't show the create new group menu items for volume aggreagtes, when we are in Qt mode. --- rman_ui/rman_ui_view3d_menus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index e9785220..b471b5ce 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -341,6 +341,8 @@ def draw(self, context): rman_vol_agg = rfb_icons.get_icon("rman_vol_aggregates") layout.operator("scene.rman_open_vol_aggregates_editor", text="Volume Aggregates Editor", icon_value=rman_vol_agg.icon_id) layout.separator() + if using_qt() and show_wip_qt(): + return op = layout.operator("renderman.add_remove_volume_aggregates", text="Create New Group") op.context="scene.renderman" op.collection="vol_aggregates" From 7f68c6c5b8aa3c0ba3ffb295592c4f73ade369fe Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 31 Mar 2023 14:03:40 -0700 Subject: [PATCH 262/278] Add WIP Qt version of the light mixer editor. --- .../rman_operators_editors_lightmixer.py | 642 +++++++++++++++++- rman_ui/rfb_qt.py | 3 + rman_ui/rman_ui_view3d_menus.py | 2 + 3 files changed, 646 insertions(+), 1 deletion(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py index 6c054939..6829decc 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py @@ -5,10 +5,640 @@ from ...rfb_utils import shadergraph_utils from ...rfb_logger import rfb_log from ... import rfb_icons +from ...rfb_utils.prefs_utils import using_qt, show_wip_qt +from ...rman_ui import rfb_qt as rfb_qt from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config import bpy import re +import sys +from PySide2 import QtCore, QtWidgets, QtGui + +__LIGHT_MIXER_WINDOW__ = None + +class LightMixerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.light_mixer_qt_app_timed" + bl_label = "RenderMan Trace Sets Editor" + + def __init__(self): + super(LightMixerQtAppTimed, self).__init__() + + def execute(self, context): + self._window = LightMixerQtWrapper() + return super(LightMixerQtAppTimed, self).execute(context) + +class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + +class ParamLabel(QtWidgets.QLabel): + def __init__(self, *args, **kwargs): + super(ParamLabel, self).__init__(*args, **kwargs) + #self.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + +class VBoxLayout(QtWidgets.QVBoxLayout): + def __init__(self, m=1, s=1): + super(VBoxLayout, self).__init__() + self.setContentsMargins(m, m, m, m) + self.setSpacing(s) + + +class HBoxLayout(QtWidgets.QHBoxLayout): + def __init__(self, m=5, s=10): + super(HBoxLayout, self).__init__() + self.setContentsMargins(m, m, m, m) + self.setSpacing(s) + +class SliderParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(SliderParam, self).__init__(parent=kwargs.get("parent", None)) + self.light_shader = None + + # build ui + self._lyt = VBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self.sl = QtWidgets.QSlider(QtCore.Qt.Vertical, self) + self.sl.setMinimum(kwargs.get("min", 0.0)) + self.sl.setMaximum(kwargs.get("max", 10.0)) + self.sl.setValue(kwargs.get("value", 0.0)) + self.sl.setTickPosition(QtWidgets.QSlider.TicksBothSides) + self.sl.setTickInterval(1.0) + self.sl.setSingleStep(kwargs.get("step", 0.1)) + self.sl.valueChanged.connect(self.slider_changed) + self._lyt.addWidget(self.sl) + + self._field = QtWidgets.QDoubleSpinBox() + self._field.setMinimum(0.0) + self._field.setMaximum(100000.0) + self._field.setSingleStep(kwargs.get("step", 0.1)) + self._field.setValue(kwargs.get("value", 0.0)) + self._field.valueChanged.connect(self.value_changed) + #self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._field) + self.setLayout(self._lyt) + + def value_changed(self, val): + val = self._field.value() + #self.sl.setValue(val) + self.update_shader(val) + + def slider_changed(self): + val = self.sl.value() + self._field.setValue(val) + self.update_shader(val) + + def update_shader(self, val): + if self.light_shader: + setattr(self.light_shader, self.param, val) + +class FloatParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(FloatParam, self).__init__(parent=kwargs.get("parent", None)) + # build ui + self._lyt = HBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self._field = QtWidgets.QDoubleSpinBox() + self._field.setMinimum(kwargs.get("min", 0.0)) + self._field.setMaximum(kwargs.get("max", 1.0)) + self._field.setSingleStep(kwargs.get("step", 0.1)) + self._field.setValue(kwargs.get("value", 0.0)) + self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._field) + self.setLayout(self._lyt) + # change cb + self._field.valueChanged.connect(self.on_change) + + @property + def value(self): + return self._field.value + + def on_change(self, val): + setattr(self.light_shader, self.param, val) + +class BoolParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(BoolParam, self).__init__(parent=kwargs.get("parent", None)) + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt = HBoxLayout(m=1, s=1) + self._lyt.addWidget(self.param_label) + self._cb = QtWidgets.QCheckBox() + self._cb.setTristate(False) + self._cb.setChecked(kwargs.get("value", False)) + self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._cb) + self.setLayout(self._lyt) + # change cb + self._cb.stateChanged.connect(self.on_change) + + @property + def value(self): + return self._cb.isChecked() + + def setChecked(self, v): + self._cb.setChecked(v) + + def on_change(self, val): + setattr(self.light_shader, self.param, bool(val)) + +class ColorButton(QtWidgets.QWidget): + colorChanged = QtCore.Signal(object) + + def __init__(self, *args, **kwargs): + super(ColorButton, self).__init__(parent=kwargs.get("parent", None)) + + self._lyt = HBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self._color = None + clr = kwargs.get("color", (0.0, 0.0, 0.0)) + self._default = QtGui.QColor.fromRgbF(clr[0], clr[1], clr[2]) + self.color_btn = QtWidgets.QPushButton() + self.color_btn.pressed.connect(self.onColorPicker) + self._lyt.addWidget(self.color_btn) + self.dlg = None + + self.setLayout(self._lyt) + + self._color = self._default + self.color_btn.setStyleSheet("background-color: %s;" % self._color.name()) + + def setColor(self, qcolor): + if qcolor != self._color: + self._color = qcolor + self.colorChanged.emit(qcolor) + + if self._color: + self.color_btn.setStyleSheet("background-color: %s;" % qcolor.name()) + else: + self.color_btn.setStyleSheet("") + + if self.light_shader: + setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) + + def color(self): + return self._color + + def onColorPicker(self): + if self.dlg is None: + self.dlg = QtWidgets.QColorDialog(self) + self.dlg.setOption(QtWidgets.QColorDialog.DontUseNativeDialog) + self.dlg.currentColorChanged.connect(self.currentColorChanged) + self.dlg.accepted.connect(self.dlg_accept) + self.dlg.rejected.connect(self.dlg_rejected) + + self.dlg.setCurrentColor(self._color) + self.dlg.open() + + def dlg_accept(self): + self.setColor(self.dlg.currentColor()) + + def dlg_rejected(self): + self.setColor(self._color) + + def currentColorChanged(self, qcolor): + if self.light_shader: + setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) + + def mousePressEvent(self, e): + if e.button() == QtCore.Qt.RightButton: + self.setColor(self._default) + + return super(ColorButton, self).mousePressEvent(e) + +class LightMixerWidget(QtWidgets.QWidget): + def __init__(self, *args, color=None, **kwargs): + super(LightMixerWidget, self).__init__(parent=kwargs.get("parent", None)) + + self._lyt = HBoxLayout() + self._simple_lyt = VBoxLayout() + self._complex_lyt = HBoxLayout() + self._lyt.addLayout(self._simple_lyt) + self._lyt.addLayout(self._complex_lyt) + self.setLayout(self._lyt) + self.simple_wgts = list() + self.complex_wgts = list() + + def add_widget(self, wgt): + self.complex_wgts.append(wgt) + self._complex_lyt.addWidget(wgt) + + def add_simple_widget(self, wgt): + self.simple_wgts.append(wgt) + self._simple_lyt.addWidget(wgt) + + def remove_widgets(self): + for w in self.simple_wgts: + self._simple_lyt.removeWidget(w) + w.deleteLater() + del w + for w in self.complex_wgts: + self._complex_lyt.removeWidget(w) + w.deleteLater() + del w + self.simple_wgts.clear() + self.complex_wgts.clear() + +class LightMixerQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(LightMixerQtWrapper, self).__init__() + + self.setWindowTitle('RenderMan Light Mixer') + self.resize(1100, 500) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.addButton.setAutoDefault(False) + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + self.removeButton.setAutoDefault(False) + + self.mixerGroupObjects = QtWidgets.QTreeView(self) + self.mixerGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.mixerGroupObjects.setModel(self.treeModel) + + self.mixerGroupObjects.setGeometry(QtCore.QRect(30, 250, 250, 192)) + self.mixerGroupObjects.setObjectName("mixerGroupObjects") + self.mixerGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.SingleSelection + ) + + self.mixerGroups = QtWidgets.QListWidget(self) + self.mixerGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.mixerGroups.setObjectName("mixerGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 200, 17)) + self.label.setText("Light Mixer Groups") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Lights") + + self.add_light_btn = QtWidgets.QPushButton(self) + self.add_light_btn.setGeometry(QtCore.QRect(279, 250, 31, 26)) + self.add_light_btn.setText("+") + self.add_light_btn.setToolTip("""Add selected lights to this mixer group""" ) + self.add_light_btn.setEnabled(False) + self.add_light_btn.setAutoDefault(False) + + self.remove_light_btn = QtWidgets.QPushButton(self) + self.remove_light_btn.setGeometry(QtCore.QRect(279, 270, 31, 26)) + self.remove_light_btn.setText("-") + self.remove_light_btn.setToolTip("""Remove selected lights""" ) + self.remove_light_btn.setEnabled(False) + self.remove_light_btn.setAutoDefault(False) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.add_light_btn.clicked.connect(self.add_light) + + self.mixerGroups.itemChanged.connect(self.trace_group_changed) + self.mixerGroups.itemSelectionChanged.connect(self.mixer_groups_index_changed) + self.mixerGroupObjects.selectionModel().selectionChanged.connect(self.mixer_group_objects_selection) + + self.light_mixer_wgt = LightMixerWidget(parent=self) + self.light_mixer_wgt.setGeometry(QtCore.QRect(340, 30, 600, 400)) + + self.refresh_groups() + self.mixerGroupObjects.expandAll() + self.enableAddLightButton() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(LightMixerQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.refresh_group_objects() + elif isinstance(dps_update.id, bpy.types.Scene): + self.enableAddLightButton() + + def enableAddLightButton(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + lights = [ob for ob in context.selected_objects if ob.type == "LIGHT"] + if not lights: + any_lights = [] + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + for ob in lights: + do_add = True + for member in grp.members: + if member.light_ob == ob: + do_add = False + break + if do_add: + any_lights.append(ob) + + self.add_light_btn.setEnabled(len(any_lights) > 0) + return + self.add_light_btn.setEnabled(True) + + def add_light(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + for ob in context.selected_objects: + do_add = True + for member in grp.members: + if member.light_ob == ob: + do_add = False + break + if do_add: + ob_in_group = grp.members.add() + ob_in_group.name = ob.name + ob_in_group.light_ob = ob + + self.refresh_group_objects() + + def checkMixerGroups(self): + if self.mixerGroups.count() < 1: + self.mixerGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.enable_trace_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.mixerGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + self.removeButton.setEnabled(True) + self.enable_trace_group_objects(self.rootNode, enable=True) + + def update(self): + idx = int(self.mixerGroups.currentRow()) + self.addButton.setEnabled(True) + + self.checkMixerGroups() + super(LightMixerQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.mixerGroups.clear() + for grp in rm.light_mixer_groups: + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.mixerGroups.addItem(item) + + if self.mixerGroups.count() > 0: + self.mixerGroups.setCurrentRow(rm.light_mixer_groups_index) + + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.light_mixer_groups.add() + grp.name = 'mixerGroup_%d' % len(rm.light_mixer_groups) + rm.light_mixer_groups_index = len(rm.light_mixer_groups)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.object_groups_index + group = rm.object_groups[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.object_groups.remove(index) + rm.object_groups_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'OBJECT'}) + + self.refresh_groups() + + def trace_group_changed(self, item): + idx = int(self.mixerGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.object_groups[idx] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.ob_pointer + ob.update_tag(refresh={'OBJECT'}) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + + return None + + def enable_trace_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.enable_trace_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.mixerGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + for member in grp.members: + ob = member.light_ob + if ob.type in ('ARMATURE', 'CAMERA'): + continue + + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + + self.mixerGroupObjects.expandAll() + #if idx != -1: + # self.mixer_groups_index_changed() + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + + def mixer_groups_index_changed(self): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + self.checkMixerGroups() + if current_item: + self.label_2.setText("Lights (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.light_mixer_groups_index = idx + + self.refresh_group_objects() + + def mixer_group_objects_selection(self, selected, deselected): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + if not current_item: + return + + context = bpy.context + scene = context.scene + rm = scene.renderman + + group_index = rm.light_mixer_groups_index + object_groups = rm.light_mixer_groups + if group_index not in range(0, len(object_groups)): + return + object_group = object_groups[group_index] + + self.light_mixer_wgt.remove_widgets() + + for i in selected.indexes(): + item = self.mixerGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + + light_shader = shadergraph_utils.get_light_node(ob) + + if light_shader.bl_label == 'PxrPortalLight': + enableTemperature = BoolParam(parent=self, + param="enableTemperature", + label="Enable Temperature", + value=light_shader.enableTemperature, + light_shader=light_shader + ) + self.light_mixer_wgt.add_simple_widget(enableTemperature) + + temperature = FloatParam(parent=self, + param="temperature", + label="Temperature", + min=1000.0, + max=50000.0, + value=light_shader.temperature, + light_shader=light_shader + ) + self.light_mixer_wgt.add_simple_widget(temperature) + + wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.intensityMult, + min=0.0, + max=10.0, + param="intensityMult", + label="Intensity Mult") + self.light_mixer_wgt.add_widget(wgt) + + + else: + exposure_wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.exposure, + min=0.0, + max=10.0, + param="exposure", + label="Exposure") + self.light_mixer_wgt.add_widget(exposure_wgt) + + wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.intensity, + min=0.0, + max=10.0, + param="intensity", + label="Intensity") + self.light_mixer_wgt.add_widget(wgt) + + if light_shader.bl_label == 'PxrEnvDayLight': + color_picker = ColorButton(parent=self, + color=light_shader.skyTint, + param="skyTint", + label="Sky Tint", + light_shader=light_shader + ) + self.light_mixer_wgt.add_simple_widget(color_picker) + else: + enableTemperature = BoolParam(parent=self, + param="enableTemperature", + label="Enable Temperature", + value=light_shader.enableTemperature, + light_shader=light_shader + ) + self.light_mixer_wgt.add_simple_widget(enableTemperature) + + temperature = FloatParam(parent=self, + param="temperature", + label="Temperature", + min=1000.0, + max=50000.0, + value=light_shader.temperature, + light_shader=light_shader + ) + self.light_mixer_wgt.add_simple_widget(temperature) + + color_picker = ColorButton(parent=self, + color=light_shader.lightColor, + param="lightColor", + label="Light Color", + light_shader=light_shader + ) + + self.light_mixer_wgt.add_simple_widget(color_picker) + + break + class RENDERMAN_UL_LightMixer_Group_Members_List(bpy.types.UIList): @@ -137,6 +767,15 @@ def __init__(self): self.event = None def invoke(self, context, event): + if using_qt() and show_wip_qt(): + global __LIGHT_MIXER_WINDOW__ + if sys.platform == "darwin": + rfb_qt.run_with_timer(__LIGHT_MIXER_WINDOW__, LightMixerQtWrapper) + else: + bpy.ops.wm.light_mixer_qt_app_timed() + + return {'FINISHED'} + wm = context.window_manager width = rfb_config['editor_preferences']['lightmixer_editor']['width'] @@ -208,7 +847,8 @@ def draw_item(self, layout, context, item): classes = [ PRMAN_OT_Renderman_Open_Light_Mixer_Editor, - RENDERMAN_UL_LightMixer_Group_Members_List + RENDERMAN_UL_LightMixer_Group_Members_List, + LightMixerQtAppTimed ] def register(): diff --git a/rman_ui/rfb_qt.py b/rman_ui/rfb_qt.py index baab9de8..1c50d157 100644 --- a/rman_ui/rfb_qt.py +++ b/rman_ui/rfb_qt.py @@ -192,6 +192,9 @@ alternate-background-color: %(alternatebg)s; min-width: 138px; } + QDoubleSpinBox { + color: %(text)s; + } ''' class RfbBaseQtAppTimed(bpy.types.Operator): diff --git a/rman_ui/rman_ui_view3d_menus.py b/rman_ui/rman_ui_view3d_menus.py index b471b5ce..b0f18ac9 100644 --- a/rman_ui/rman_ui_view3d_menus.py +++ b/rman_ui/rman_ui_view3d_menus.py @@ -523,6 +523,8 @@ def draw(self, context): scene = context.scene layout.operator('scene.rman_open_light_mixer_editor', text='Light Mixer Editor') layout.separator() + if using_qt() and show_wip_qt(): + return selected_light_objects = [] if context.selected_objects: for obj in context.selected_objects: From 48eda19fefaf5faed35d9e7d456e86ef648d483d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Mon, 3 Apr 2023 08:35:12 -0700 Subject: [PATCH 263/278] More work for the Qt version of the light mixer. * we now display all of the lights in the group, rather than just the selected light * all parameters are now displayed horizontally, including the float sliders --- .../rman_operators_editors_lightmixer.py | 240 +++++++++--------- 1 file changed, 118 insertions(+), 122 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py index 6829decc..018ab2d1 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py @@ -57,12 +57,12 @@ def __init__(self, *args, **kwargs): self.light_shader = None # build ui - self._lyt = VBoxLayout() + self._lyt = HBoxLayout() self.light_shader = kwargs.get("light_shader", None) self.param = kwargs.get("param", "") self.param_label = ParamLabel(kwargs.get("label", "label")) self._lyt.addWidget(self.param_label) - self.sl = QtWidgets.QSlider(QtCore.Qt.Vertical, self) + self.sl = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) self.sl.setMinimum(kwargs.get("min", 0.0)) self.sl.setMaximum(kwargs.get("max", 10.0)) self.sl.setValue(kwargs.get("value", 0.0)) @@ -218,37 +218,54 @@ def mousePressEvent(self, e): return super(ColorButton, self).mousePressEvent(e) class LightMixerWidget(QtWidgets.QWidget): - def __init__(self, *args, color=None, **kwargs): + def __init__(self, *args, **kwargs): super(LightMixerWidget, self).__init__(parent=kwargs.get("parent", None)) - self._lyt = HBoxLayout() - self._simple_lyt = VBoxLayout() - self._complex_lyt = HBoxLayout() - self._lyt.addLayout(self._simple_lyt) - self._lyt.addLayout(self._complex_lyt) + self._lyt = VBoxLayout(m=5) + self._lyt.setAlignment(QtCore.Qt.AlignTop) + self._lbl = QtWidgets.QLabel() + self._lbl.setAlignment(QtCore.Qt.AlignHCenter) + lbl = kwargs.get("name", "Light") + self._lbl.setText(lbl) + self._lyt.addWidget(self._lbl) self.setLayout(self._lyt) - self.simple_wgts = list() - self.complex_wgts = list() + + def __del__(self): + self.remove_widgets() def add_widget(self, wgt): - self.complex_wgts.append(wgt) - self._complex_lyt.addWidget(wgt) + self._lyt.addWidget(wgt) - def add_simple_widget(self, wgt): - self.simple_wgts.append(wgt) - self._simple_lyt.addWidget(wgt) + def remove_widgets(self): + for i in reversed(range(self._lyt.count())): + w = self._lyt.takeAt(i).widget() + if w is not None: + w.setParent(None) + w.deleteLater() + +class LightMixerLayout(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(LightMixerLayout, self).__init__(parent=kwargs.get("parent", None)) + + self.groupbox = QtWidgets.QGroupBox('') + self._gb_lyt = VBoxLayout() + self.groupbox.setLayout(self._gb_lyt) + scroll = QtWidgets.QScrollArea() + scroll.setWidget(self.groupbox) + scroll.setWidgetResizable(True) + self._lyt = VBoxLayout() + self._lyt.addWidget(scroll) + self.setLayout(self._lyt) + + def add_widget(self, wgt): + self._gb_lyt.addWidget(wgt) def remove_widgets(self): - for w in self.simple_wgts: - self._simple_lyt.removeWidget(w) - w.deleteLater() - del w - for w in self.complex_wgts: - self._complex_lyt.removeWidget(w) - w.deleteLater() - del w - self.simple_wgts.clear() - self.complex_wgts.clear() + for i in reversed(range(self._gb_lyt.count())): + w = self._gb_lyt.takeAt(i).widget() + if w is not None: + w.setParent(None) + w.deleteLater() class LightMixerQtWrapper(rfb_qt.RmanQtWrapper): def __init__(self) -> None: @@ -281,14 +298,14 @@ def __init__(self) -> None: self.rootNode = self.treeModel.invisibleRootItem() self.mixerGroupObjects.setModel(self.treeModel) - self.mixerGroupObjects.setGeometry(QtCore.QRect(30, 250, 250, 192)) + self.mixerGroupObjects.setGeometry(QtCore.QRect(30, 140, 250, 350)) self.mixerGroupObjects.setObjectName("mixerGroupObjects") self.mixerGroupObjects.setSelectionMode( QtWidgets.QAbstractItemView.SingleSelection ) self.mixerGroups = QtWidgets.QListWidget(self) - self.mixerGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.mixerGroups.setGeometry(QtCore.QRect(30, 30, 256, 80)) self.mixerGroups.setObjectName("mixerGroups") self.label = QtWidgets.QLabel(self) @@ -296,18 +313,18 @@ def __init__(self) -> None: self.label.setText("Light Mixer Groups") self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setGeometry(QtCore.QRect(40, 120, 200, 17)) self.label_2.setText("Lights") self.add_light_btn = QtWidgets.QPushButton(self) - self.add_light_btn.setGeometry(QtCore.QRect(279, 250, 31, 26)) + self.add_light_btn.setGeometry(QtCore.QRect(279, 140, 31, 26)) self.add_light_btn.setText("+") self.add_light_btn.setToolTip("""Add selected lights to this mixer group""" ) self.add_light_btn.setEnabled(False) self.add_light_btn.setAutoDefault(False) self.remove_light_btn = QtWidgets.QPushButton(self) - self.remove_light_btn.setGeometry(QtCore.QRect(279, 270, 31, 26)) + self.remove_light_btn.setGeometry(QtCore.QRect(279, 160, 31, 26)) self.remove_light_btn.setText("-") self.remove_light_btn.setToolTip("""Remove selected lights""" ) self.remove_light_btn.setEnabled(False) @@ -316,12 +333,13 @@ def __init__(self) -> None: self.addButton.clicked.connect(self.add_group) self.removeButton.clicked.connect(self.remove_group) self.add_light_btn.clicked.connect(self.add_light) + self.remove_light_btn.clicked.connect(self.remove_light) - self.mixerGroups.itemChanged.connect(self.trace_group_changed) + self.mixerGroups.itemChanged.connect(self.mixer_group_changed) self.mixerGroups.itemSelectionChanged.connect(self.mixer_groups_index_changed) self.mixerGroupObjects.selectionModel().selectionChanged.connect(self.mixer_group_objects_selection) - self.light_mixer_wgt = LightMixerWidget(parent=self) + self.light_mixer_wgt = LightMixerLayout(parent=self) self.light_mixer_wgt.setGeometry(QtCore.QRect(340, 30, 600, 400)) self.refresh_groups() @@ -387,23 +405,38 @@ def add_light(self): ob_in_group.name = ob.name ob_in_group.light_ob = ob - self.refresh_group_objects() + self.refresh_group_objects() - def checkMixerGroups(self): - if self.mixerGroups.count() < 1: - self.mixerGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.enable_trace_group_objects(self.rootNode, enable=False) - self.removeButton.setEnabled(False) - else: - self.mixerGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.removeButton.setEnabled(True) - self.enable_trace_group_objects(self.rootNode, enable=True) + def remove_light(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + + index = self.mixerGroupObjects.selectedIndexes()[0] + item = index.model().itemFromIndex(index) + light_nm = item.text() + ob = context.scene.objects.get(light_nm, None) + if not ob: + return + + do_remove = False + idx = -1 + for i, member in enumerate(grp.members): + if ob == member.light_ob: + do_remove = True + idx = i + break + if do_remove: + member = grp.members.remove(idx) + + self.refresh_group_objects() + def update(self): idx = int(self.mixerGroups.currentRow()) self.addButton.setEnabled(True) - self.checkMixerGroups() super(LightMixerQtWrapper, self).update() def refresh_groups(self): @@ -447,17 +480,17 @@ def remove_group(self): self.refresh_groups() - def trace_group_changed(self, item): + def mixer_group_changed(self, item): idx = int(self.mixerGroups.currentRow()) context = bpy.context scene = context.scene rm = scene.renderman - grp = rm.object_groups[idx] + grp = rm.light_mixer_groups[idx] grp.name = item.text() self.label_2.setText("Objects (%s)" % item.text()) for member in grp.members: - ob = member.ob_pointer + ob = member.light_ob ob.update_tag(refresh={'OBJECT'}) def find_item(self, standard_item, ob): @@ -467,21 +500,13 @@ def find_item(self, standard_item, ob): return item return None - - def enable_trace_group_objects(self, standard_item, enable=True): - standard_item.setEnabled(enable) - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - item.setEnabled(enable) - if item.hasChildren(): - return self.enable_trace_group_objects(item, enable=enable) def refresh_group_objects(self): + self.light_mixer_wgt.remove_widgets() idx = int(self.mixerGroups.currentRow()) - enabled = True if idx == -1: - enabled = False self.label_2.setText("Objects (no group selected)") + context = bpy.context scene = context.scene @@ -494,64 +519,12 @@ def refresh_group_objects(self): for member in grp.members: ob = member.light_ob - if ob.type in ('ARMATURE', 'CAMERA'): - continue - item = StandardItem(txt=ob.name) + light_shader = shadergraph_utils.get_light_node(ob) + item = StandardItem(txt=ob.name) self.rootNode.appendRow(item) - self.mixerGroupObjects.expandAll() - #if idx != -1: - # self.mixer_groups_index_changed() - - def bl_select_objects(self, obs): - context = bpy.context - for ob in context.selected_objects: - ob.select_set(False) - for ob in obs: - ob.select_set(True) - context.view_layer.objects.active = ob - - def mixer_groups_index_changed(self): - idx = int(self.mixerGroups.currentRow()) - current_item = self.mixerGroups.currentItem() - self.checkMixerGroups() - if current_item: - self.label_2.setText("Lights (%s)" % current_item.text()) - else: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.light_mixer_groups_index = idx - - self.refresh_group_objects() - - def mixer_group_objects_selection(self, selected, deselected): - idx = int(self.mixerGroups.currentRow()) - current_item = self.mixerGroups.currentItem() - if not current_item: - return - - context = bpy.context - scene = context.scene - rm = scene.renderman - - group_index = rm.light_mixer_groups_index - object_groups = rm.light_mixer_groups - if group_index not in range(0, len(object_groups)): - return - object_group = object_groups[group_index] - - self.light_mixer_wgt.remove_widgets() - - for i in selected.indexes(): - item = self.mixerGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - - light_shader = shadergraph_utils.get_light_node(ob) + lgt_mixer_wgt = LightMixerWidget(parent=self, name=ob.name) if light_shader.bl_label == 'PxrPortalLight': enableTemperature = BoolParam(parent=self, @@ -560,7 +533,7 @@ def mixer_group_objects_selection(self, selected, deselected): value=light_shader.enableTemperature, light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(enableTemperature) + lgt_mixer_wgt.add_widget(enableTemperature) temperature = FloatParam(parent=self, param="temperature", @@ -570,7 +543,7 @@ def mixer_group_objects_selection(self, selected, deselected): value=light_shader.temperature, light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(temperature) + lgt_mixer_wgt.add_widget(temperature) wgt = SliderParam(parent=self, light_shader=light_shader, @@ -579,7 +552,7 @@ def mixer_group_objects_selection(self, selected, deselected): max=10.0, param="intensityMult", label="Intensity Mult") - self.light_mixer_wgt.add_widget(wgt) + lgt_mixer_wgt.add_widget(wgt) else: @@ -590,7 +563,7 @@ def mixer_group_objects_selection(self, selected, deselected): max=10.0, param="exposure", label="Exposure") - self.light_mixer_wgt.add_widget(exposure_wgt) + lgt_mixer_wgt.add_widget(exposure_wgt) wgt = SliderParam(parent=self, light_shader=light_shader, @@ -599,7 +572,7 @@ def mixer_group_objects_selection(self, selected, deselected): max=10.0, param="intensity", label="Intensity") - self.light_mixer_wgt.add_widget(wgt) + lgt_mixer_wgt.add_widget(wgt) if light_shader.bl_label == 'PxrEnvDayLight': color_picker = ColorButton(parent=self, @@ -608,7 +581,7 @@ def mixer_group_objects_selection(self, selected, deselected): label="Sky Tint", light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(color_picker) + lgt_mixer_wgt.add_widget(color_picker) else: enableTemperature = BoolParam(parent=self, param="enableTemperature", @@ -616,7 +589,7 @@ def mixer_group_objects_selection(self, selected, deselected): value=light_shader.enableTemperature, light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(enableTemperature) + lgt_mixer_wgt.add_widget(enableTemperature) temperature = FloatParam(parent=self, param="temperature", @@ -626,7 +599,7 @@ def mixer_group_objects_selection(self, selected, deselected): value=light_shader.temperature, light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(temperature) + lgt_mixer_wgt.add_widget(temperature) color_picker = ColorButton(parent=self, color=light_shader.lightColor, @@ -635,10 +608,33 @@ def mixer_group_objects_selection(self, selected, deselected): light_shader=light_shader ) - self.light_mixer_wgt.add_simple_widget(color_picker) + lgt_mixer_wgt.add_widget(color_picker) + + self.light_mixer_wgt.add_widget(lgt_mixer_wgt) - break - + self.mixerGroupObjects.expandAll() + + def mixer_groups_index_changed(self): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + if current_item: + self.label_2.setText("Lights (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.light_mixer_groups_index = idx + + self.refresh_group_objects() + + def mixer_group_objects_selection(self, selected, deselected): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + if not current_item: + self.remove_light_btn.setEnabled(False) + return + self.remove_light_btn.setEnabled(True) class RENDERMAN_UL_LightMixer_Group_Members_List(bpy.types.UIList): From 6efa924cb126bb9c7a29c30d43d4cbd16b0da89d Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Apr 2023 07:52:13 -0700 Subject: [PATCH 264/278] Fix a couple of issues with light linking not updating correctly when using the Qt version of the editor. --- preferences.py | 2 +- rfb_utils/scene_utils.py | 13 ++++---- .../rman_operators_editors_lightlink.py | 30 +++++++++++++------ .../rman_properties_misc/__init__.py | 4 +++ rman_translators/rman_light_translator.py | 2 +- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/preferences.py b/preferences.py index 3a76bc35..185fb5be 100644 --- a/preferences.py +++ b/preferences.py @@ -405,7 +405,7 @@ def update_rman_logging_level(self, context): rman_invert_light_linking: BoolProperty( name="Invert Light Linking", default=False, - description="Invert the behavior of light linking. Only objects linked to the light in the light linking editor will be illuminated. Changing this requires an IPR restart.", + description="Invert the behavior of light linking (only applies if UI framework is set to Native). Only objects linked to the light in the light linking editor will be illuminated. Changing this requires an IPR restart.", ) rman_show_cycles_convert: BoolProperty( diff --git a/rfb_utils/scene_utils.py b/rfb_utils/scene_utils.py index eff436c2..4a325557 100644 --- a/rfb_utils/scene_utils.py +++ b/rfb_utils/scene_utils.py @@ -473,12 +473,13 @@ def find_node_by_name(node_name, ob_name, library=''): return (None, None) -def set_lightlinking_properties(ob, light_ob, illuminate): +def set_lightlinking_properties(ob, light_ob, illuminate, update_light=True): light_props = shadergraph_utils.get_rman_light_properties_group(light_ob) if light_props.renderman_light_role not in {'RMAN_LIGHTFILTER', 'RMAN_LIGHT'}: return - light_ob.update_tag(refresh={'DATA'}) + if update_light: + light_ob.update_tag(refresh={'DATA'}) changed = False if light_props.renderman_light_role == 'RMAN_LIGHT': exclude_subset = [] @@ -487,7 +488,7 @@ def set_lightlinking_properties(ob, light_ob, illuminate): for j, subset in enumerate(ob.renderman.rman_lighting_excludesubset): if subset.light_ob == light_ob: do_add = False - exclude_subset.append('%s' % string_utils.sanitize_node_name(light_ob.name_full)) + exclude_subset.append('%s' % string_utils.sanitize_node_name(subset.light_ob.name_full)) if do_add: subset = ob.renderman.rman_lighting_excludesubset.add() subset.name = light_ob.name @@ -501,7 +502,7 @@ def set_lightlinking_properties(ob, light_ob, illuminate): changed = True idx = j else: - exclude_subset.append('%s' % string_utils.sanitize_node_name(light_ob.name_full)) + exclude_subset.append('%s' % string_utils.sanitize_node_name(subset.light_ob.name_full)) if changed: ob.renderman.rman_lighting_excludesubset.remove(j) ob.renderman.rman_lighting_excludesubset_string = ','.join(exclude_subset) @@ -512,7 +513,7 @@ def set_lightlinking_properties(ob, light_ob, illuminate): for j, subset in enumerate(ob.renderman.rman_lightfilter_subset): if subset.light_ob == light_ob: do_add = False - lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(light_ob.name_full)) + lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(subset.light_ob.name_full)) if do_add: subset = ob.renderman.rman_lightfilter_subset.add() @@ -527,7 +528,7 @@ def set_lightlinking_properties(ob, light_ob, illuminate): changed = True idx = j else: - lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(light_ob.name_full)) + lightfilter_subset.append('-%s' % string_utils.sanitize_node_name(subset.light_ob.name_full)) if changed: ob.renderman.rman_lightfilter_subset.remove(idx) ob.renderman.rman_lightfilter_subset_string = ','.join(lightfilter_subset) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 72a433b2..04be8b37 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -127,7 +127,7 @@ def depsgraph_update_post(self, bl_scene, depsgraph): if isinstance(dps_update.id, bpy.types.Collection): self.refresh_lights() self.refresh_linked_objects() - self.lights_index_changed() + #self.lights_index_changed() #elif isinstance(dps_update.id, bpy.types.Scene): # self.refresh_lights() @@ -185,7 +185,15 @@ def get_all_object_items(self, standard_item, items): item = standard_item.child(i) items.append(item) if item.rowCount() > 0: - self.get_all_object_items(item, items) + self.get_all_object_items(item, items) + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob def lights_index_changed(self): idx = int(self.lights_treeView.currentRow()) @@ -200,8 +208,11 @@ def lights_index_changed(self): context = bpy.context scene = context.scene rm = scene.renderman + rm.light_links_index = idx light_nm = current_item.text() light_ob = context.scene.objects.get(light_nm, None) + if light_ob: + self.bl_select_objects([light_ob]) light_link_item = self.find_light_link_item(light_nm) selected_items = QtCore.QItemSelection() @@ -209,10 +220,12 @@ def lights_index_changed(self): self.get_all_object_items(self.rootNode, items) if light_link_item is None: + ''' if not object_utils.is_light_filter(light_ob): light_link_item = scene.renderman.light_links.add() light_link_item.name = light_ob.name light_link_item.light_ob = light_ob + ''' for item in items: idx = self.treeModel.indexFromItem(item) @@ -313,12 +326,13 @@ def linked_objects_selection(self, selected, deselected): light_ob = context.scene.objects.get(light_nm, None) light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' - ll = self.find_light_link_item(light_nm) + ll = self.find_light_link_item(light_nm) if ll is None: ll = scene.renderman.light_links.add() ll.name = light_ob.name - ll.light_ob = light_ob + ll.light_ob = light_ob + rm.lights_link_index = len(rm.light_links)-1 if is_light_filter: # linkingGroups should only be set if one of the items is deselected @@ -347,8 +361,7 @@ def linked_objects_selection(self, selected, deselected): member.name = ob.name member.ob_pointer = ob member.illuminate = 'OFF' - - scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) + scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) for i in selected.indexes(): item = self.objects_treeView.model().itemFromIndex(i) @@ -363,9 +376,8 @@ def linked_objects_selection(self, selected, deselected): idx = i break if do_remove: - member = ll.members.remove(idx) - - scene_utils.set_lightlinking_properties(ob, light_ob, '') + member = ll.members.remove(idx) + scene_utils.set_lightlinking_properties(ob, light_ob, '') class RENDERMAN_UL_LightLink_Light_List(bpy.types.UIList): diff --git a/rman_properties/rman_properties_misc/__init__.py b/rman_properties/rman_properties_misc/__init__.py index 56492215..789e1870 100644 --- a/rman_properties/rman_properties_misc/__init__.py +++ b/rman_properties/rman_properties_misc/__init__.py @@ -70,12 +70,16 @@ def update_ob_pointer(self, context): def update_link(self, context): light_ob = getattr(context, 'light_ob', None) + if not light_ob or light_ob.type != 'LIGHT': + return + ''' if not light_ob and hasattr(context, 'active_object'): light_ob = context.active_object if light_ob and light_ob.type != 'LIGHT': return if not light_ob: return + ''' ob = self.ob_pointer if scene_utils.set_lightlinking_properties(ob, light_ob, self.illuminate): diff --git a/rman_translators/rman_light_translator.py b/rman_translators/rman_light_translator.py index 1fa5f856..dc95a39a 100644 --- a/rman_translators/rman_light_translator.py +++ b/rman_translators/rman_light_translator.py @@ -68,7 +68,7 @@ def update_light_attributes(self, ob, rman_sg_light): attrs.SetInteger("visibility:camera", int(primary_vis)) attrs.SetInteger("visibility:transmission", 0) attrs.SetInteger("visibility:indirect", 0) - obj_groups_str = "World,%s" % ob.name_full + obj_groups_str = "World,%s" % string_utils.sanitize_node_name(ob.name_full) attrs.SetString(self.rman_scene.rman.Tokens.Rix.k_grouping_membership, obj_groups_str) rman_sg_light.sg_node.SetAttributes(attrs) From 9ebd15075ca9f698242e7dbdb8c8dffd611a713a Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Apr 2023 08:29:20 -0700 Subject: [PATCH 265/278] For RMAN-20525 Change the specified bounds for VDB to be: [-1e30, 1e30, -1e30, 1e30, -1e30, 1e30] The renderer is guaranteed to get the right bounds from the VDB file itself. --- rman_translators/rman_openvdb_translator.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rman_translators/rman_openvdb_translator.py b/rman_translators/rman_openvdb_translator.py index 82d64319..196022b4 100644 --- a/rman_translators/rman_openvdb_translator.py +++ b/rman_translators/rman_openvdb_translator.py @@ -43,9 +43,9 @@ def update(self, ob, rman_sg_openvdb): primvar = rman_sg_openvdb.sg_node.GetPrimVars() primvar.Clear() bounds = transform_utils.convert_ob_bounds(ob.bound_box) - primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, string_utils.convert_val(bounds), 6) if db.filepath == '': primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "box") + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, string_utils.convert_val(bounds), 6) rman_sg_openvdb.sg_node.SetPrimVars(primvar) return @@ -54,12 +54,14 @@ def update(self, ob, rman_sg_openvdb): if not grids.load(): rfb_log().error("Could not load grids and metadata for volume: %s" % ob.name) primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "box") + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, string_utils.convert_val(bounds), 6) rman_sg_openvdb.sg_node.SetPrimVars(primvar) return if len(grids) < 1: rfb_log().error("Grids length=0: %s" % ob.name) primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "box") + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, string_utils.convert_val(bounds), 6) rman_sg_openvdb.sg_node.SetPrimVars(primvar) return @@ -68,8 +70,11 @@ def update(self, ob, rman_sg_openvdb): if active_grid.data_type not in ['FLOAT', 'DOUBLE']: rfb_log().error("Active grid is not of float type: %s" % ob.name) primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "box") + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, string_utils.convert_val(bounds), 6) rman_sg_openvdb.sg_node.SetPrimVars(primvar) return + + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, [-1e30, 1e30, -1e30, 1e30, -1e30, 1e30], 6) openvdb_file = filepath_utils.get_real_path(db.filepath) From d5ba0fda996485744166b9565a1ee2946400cb71 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Apr 2023 08:32:55 -0700 Subject: [PATCH 266/278] Even though, we currently don't use the OpenVDB in this case, update the bounds for the fluid translator as well. --- rman_translators/rman_fluid_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_translators/rman_fluid_translator.py b/rman_translators/rman_fluid_translator.py index 32e25da3..bfa8bd1c 100644 --- a/rman_translators/rman_fluid_translator.py +++ b/rman_translators/rman_fluid_translator.py @@ -193,7 +193,7 @@ def update_fluid_openvdb(self, ob, rman_sg_fluid, fluid_data): primvar = rman_sg_fluid.rman_sg_volume_node.GetPrimVars() primvar.SetString(self.rman_scene.rman.Tokens.Rix.k_Ri_type, "blobbydso:impl_openvdb") - primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, transform_utils.convert_ob_bounds(ob.bound_box), 6) + primvar.SetFloatArray(self.rman_scene.rman.Tokens.Rix.k_Ri_Bound, [-1e30, 1e30, -1e30, 1e30, -1e30, 1e30], 6) primvar.SetStringArray(self.rman_scene.rman.Tokens.Rix.k_blobbydso_stringargs, [cacheFile, "density:fogvolume"], 2) primvar.SetFloatDetail("density", [], "varying") From cd1b44c5127f3ba6049d75f28aad51c40d4dc488 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Apr 2023 11:33:53 -0700 Subject: [PATCH 267/278] Add tractor settings to our addon preferences. The settings can still be overriden via JSON config files. --- preferences.py | 202 +++++++++++++++++++++- rman_config/config/rfb.json | 3 - rman_config/config/schemas/rfbSchema.json | 39 ++++- rman_scene.py | 5 + rman_spool.py | 76 +++++++- 5 files changed, 309 insertions(+), 16 deletions(-) diff --git a/preferences.py b/preferences.py index 185fb5be..fd3cd13c 100644 --- a/preferences.py +++ b/preferences.py @@ -92,12 +92,70 @@ 'rman_roz_webSocketServer_Port': 0, 'rman_roz_stats_print_level': '1', 'rman_enhance_zoom_factor': 5, - 'rman_parent_lightfilter': False + 'rman_parent_lightfilter': False, + 'rman_tractor_hostname': 'tractor-engine', + 'rman_tractor_port': 80, + 'rman_tractor_local_user': True, + 'rman_tractor_user': '', + 'rman_tractor_priority': 1.0, + 'rman_tractor_service': 'PixarRender', + 'rman_tractor_envkeys': '', + 'rman_tractor_after': '', + 'rman_tractor_crews': '', + 'rman_tractor_tier': '', + 'rman_tractor_projects': '', + 'rman_tractor_comment': '', + 'rman_tractor_metadata': '', + 'rman_tractor_whendone': '', + 'rman_tractor_whenerror': '', + 'rman_tractor_whenalways': '', + 'rman_tractor_dirmaps': [] } class RendermanPreferencePath(bpy.types.PropertyGroup): path: StringProperty(name="", subtype='DIR_PATH') +class PRMAN_OT_add_dirmap(bpy.types.Operator): + bl_idname = "renderman.add_dirmap" + bl_label = "Add Dirmap" + bl_description = "Add a new dirmap" + + def execute(self, context): + addon = context.preferences.addons[__package__] + prefs = addon.preferences + dirmap = prefs.rman_tractor_dirmaps.add() + + return {'FINISHED'} + +class PRMAN_OT_remove_dirmap(bpy.types.Operator): + bl_idname = "renderman.remove_dirmap" + bl_label = "Remove Dirmap" + bl_description = "Remove a dirmap" + + index: IntProperty( + default=0 + ) + + def execute(self, context): + addon = context.preferences.addons[__package__] + prefs = addon.preferences + if self.properties.index < len(prefs.rman_tractor_dirmaps): + prefs.rman_tractor_dirmaps.remove(self.properties.index) + + return {'FINISHED'} + +class RendermanDirMap(bpy.types.PropertyGroup): + from_path: StringProperty(name="From", description="") + to_path: StringProperty(name="To", description="") + zone: EnumProperty( + name="Zone", + description="The zone that this dirmap should apply to. UNC is for Windows; NFS is for linux and macOS.", + default="NFS", + items=[('NFS', 'NFS', ''), + ('UNC', 'UNC', '') + ] + ) + class RendermanDeviceDesc(bpy.types.PropertyGroup): name: StringProperty(name="", default="") id: IntProperty(default=-1) @@ -545,7 +603,106 @@ def update_stats_config(self, context): name="Parent Filter to Light", default=False, description="If on, and a light is selected, attaching a light filter will parent the light filter to the selected light." - ) + ) + + # Tractor preferences + rman_tractor_hostname: StringProperty( + name="Hostname", + default="tractor-engine", + description="Hostname of the Tractor engine to use to submit batch render jobs" + ) + + rman_tractor_port: IntProperty( + name="Port", + default=80, + description="Port number that the Tractor engine is listening on" + ) + + rman_tractor_local_user: BoolProperty( + name="Use Local User", + default=True, + description="Use the current logged in user to submit the tractor job" + ) + + rman_tractor_user: StringProperty( + name="Username", + default="", + description="Username to use to submit the tractor job" + ) + + rman_tractor_priority: FloatProperty( + name="Priority", + default=1.0, + description="Priority of your job" + ) + + rman_tractor_service: StringProperty( + name="Service", + default="PixarRender", + description="Service keys for your job" + ) + + rman_tractor_envkeys: StringProperty( + name="Environment Keys", + default="", + description="Multiple keys can be specified and should be space separated.", + ) + + rman_tractor_after: StringProperty( + name="After", + default="", + description="Delay start of job processing until given time\nFormat: MONTH/DAY HOUR:MINUTES\nEx: 11/24 13:45" + ) + + rman_tractor_crews: StringProperty( + name="Crews", + default="", + description="List of crews. See 'Crews' in the Tractor documentation", + ) + + rman_tractor_tier: StringProperty( + name="Tier", + default="", + description="Dispatching tier that the job belongs to. See 'Scheduling Modes' in the Tractor documentation" + ) + + rman_tractor_projects: StringProperty( + name="Projects", + default="", + description="Dispatching tier that the job belongs to. See 'Limits Configuration' in the Tractor documentation" + ) + + rman_tractor_comment: StringProperty( + name="Comments", + default="", + description="Additional comment about the job." + ) + + rman_tractor_metadata: StringProperty( + name="Meta Data", + default="", + description= "Meta data to add to the job." + ) + + rman_tractor_whendone: StringProperty( + name='When Done Command', + default='', + description="Command to run when job completes withour error." + ) + + rman_tractor_whenerror: StringProperty( + name='When Error Command', + default='', + description="Command to run if there is an error executing the job." + ) + + rman_tractor_whenalways: StringProperty( + name='When Always Command', + default='', + description="Command to run regardless if job completes with or without errors." + ) + + rman_tractor_dirmaps: bpy.props.CollectionProperty(type=RendermanDirMap) def draw_xpu_devices(self, context, layout): if self.rman_xpu_device == 'CPU': @@ -673,6 +830,44 @@ def draw(self, context): col.prop(self, 'rman_logging_level') col.prop(self, 'rman_logging_file') + # Batch Rendering + row = layout.row() + row.label(text='Batch Rendering', icon_value=rman_r_icon.icon_id) + row = layout.row() + col = row.column() + col.prop(self, 'rman_tractor_hostname') + col.prop(self, 'rman_tractor_port') + col.prop(self, 'rman_tractor_local_user') + if not self.rman_tractor_local_user: + col.prop(self, 'rman_tractor_user') + col.prop(self, 'rman_tractor_priority') + col.prop(self, 'rman_tractor_service') + col.prop(self, 'rman_tractor_envkeys') + col.prop(self, 'rman_tractor_after') + col.prop(self, 'rman_tractor_crews') + col.prop(self, 'rman_tractor_tier') + col.prop(self, 'rman_tractor_projects') + col.prop(self, 'rman_tractor_comment') + col.prop(self, 'rman_tractor_metadata') + col.prop(self, 'rman_tractor_whendone') + col.prop(self, 'rman_tractor_whenerror') + col.prop(self, 'rman_tractor_whenalways') + + row = layout.row() + row.label(text='Directory Maps') + row = layout.row() + col = row.column() + col.operator('renderman.add_dirmap', text='+') + for i, dirmap in enumerate(self.rman_tractor_dirmaps): + dirmap_row = col.row() + dirmap_row.use_property_split = False + dirmap_row.use_property_decorate = True + dirmap_row.prop(dirmap, 'from_path') + dirmap_row.prop(dirmap, 'to_path') + dirmap_row.prop(dirmap, 'zone') + op = dirmap_row.operator('renderman.remove_dirmap', text='X') + op.index = i + # Advanced row = layout.row() row.use_property_split = False @@ -724,6 +919,9 @@ def draw(self, context): classes = [ RendermanPreferencePath, RendermanDeviceDesc, + PRMAN_OT_add_dirmap, + PRMAN_OT_remove_dirmap, + RendermanDirMap, RendermanPreferences ] diff --git a/rman_config/config/rfb.json b/rman_config/config/rfb.json index 299ad374..05994223 100644 --- a/rman_config/config/rfb.json +++ b/rman_config/config/rfb.json @@ -1,9 +1,6 @@ { "$schema": "./schemas/rfbSchema.json", "tractor_cfg": { - "engine": "tractor-engine", - "port": 80, - "user": "" }, "dirmaps": { }, diff --git a/rman_config/config/schemas/rfbSchema.json b/rman_config/config/schemas/rfbSchema.json index cbe4c14b..aada99b5 100644 --- a/rman_config/config/schemas/rfbSchema.json +++ b/rman_config/config/schemas/rfbSchema.json @@ -35,11 +35,46 @@ }, "user": { "type": "string" + }, + "priority": { + "type": "number" + }, + "service": { + "type": "string" + }, + "envkeys": { + "type": "string" + }, + "after": { + "type": "string" + }, + "crews": { + "type": "string" + }, + "tier": { + "type": "string" + }, + "projects": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "metdata": { + "type": "string" + }, + "whendone": { + "type": "string" + }, + "whenerror": { + "type": "string" + }, + "whenalways": { + "type": "string" } + }, "required": [ - "engine", - "port" ] }, "bxdf_viewport_color_map": { diff --git a/rman_scene.py b/rman_scene.py index fbe01bc5..3bdcf335 100644 --- a/rman_scene.py +++ b/rman_scene.py @@ -1219,6 +1219,11 @@ def export_global_options(self): # dirmaps dirmaps = '' + prefs_dirmaps = get_pref('rman_tractor_dirmaps', []) + for dirmap in prefs_dirmaps: + d = "[ \"%s\" \"%s\" \"%s\"]" % (dirmap.zone, dirmap.from_path, dirmap.to_path) + dirmaps += d + for k in rfb_config['dirmaps']: dirmap = rfb_config['dirmaps'][k] d = "[ \"%s\" \"%s\" \"%s\"]" % (dirmap['zone'], dirmap['from'], dirmap['to']) diff --git a/rman_spool.py b/rman_spool.py index a45e5b25..c20391fd 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -3,6 +3,7 @@ import getpass import socket import platform +import datetime import bpy from .rfb_utils import filepath_utils from .rfb_utils import string_utils @@ -23,6 +24,7 @@ def __init__(self, rman_render, rman_scene, depsgraph): self.is_localqueue = True self.is_tractor = False self.any_denoise = False + self.tractor_cfg = rfb_config['tractor_cfg'] if depsgraph: self.bl_scene = depsgraph.scene_eval self.depsgraph = depsgraph @@ -30,6 +32,12 @@ def __init__(self, rman_render, rman_scene, depsgraph): self.is_tractor = (self.bl_scene.renderman.queuing_system == 'tractor') def add_job_level_attrs(self, job): + dirmaps = get_pref('rman_tractor_dirmaps', []) + for dirmap in dirmaps: + job.newDirMap(src=dirmap.from_path, + dst=dirmap.to_path, + zone=dirmap.zone) + for k in rfb_config['dirmaps']: dirmap = rfb_config['dirmaps'][k] job.newDirMap(src=str(dirmap['from']), @@ -37,10 +45,57 @@ def add_job_level_attrs(self, job): zone=str(dirmap['zone'])) rman_vers = envconfig().build_info.version() + envkeys = [] if self.is_localqueue: - job.envkey = ['rmantree=%s' % envconfig().rmantree] + envkeys.append('rmantree=%s' % envconfig().rmantree) else: - job.envkey = ['prman-%s' % rman_vers] + envkeys.append('prman-%s' % rman_vers) + + user_envkeys = self.tractor_cfg.get('envkeys', get_pref('rman_tractor_envkeys')) + job.envkey = envkeys + user_envkeys.split() + + service = self.tractor_cfg.get('service', get_pref('rman_tractor_service')) + job.service = service + job.priority = float(self.tractor_cfg.get('priority', get_pref('rman_tractor_priority'))) + job.metadata = self.tractor_cfg.get('metadata', get_pref('rman_tractor_metadata')) + job.comment = self.tractor_cfg.get('comment', get_pref('rman_tractor_comment')) + + crews = self.tractor_cfg.get('crews', get_pref('rman_tractor_crews')) + if crews != '': + job.crews = crews.split() + projects = self.tractor_cfg.get('projects', get_pref('rman_tractor_projects')) + if projects != '': + job.projects = projects.split() + + whendone = self.tractor_cfg.get('whendone', get_pref('rman_tractor_whendone')) + whenerror = self.tractor_cfg.get('whenerror', get_pref('rman_tractor_whenerror')) + whenalways = self.tractor_cfg.get('whenalways', get_pref('rman_tractor_whenalways')) + if whendone != '': + job.newPostscript(argv=whendone, when="done", service=service) + if whenerror != '': + job.newPostscript(argv=whenerror, when="error", service=service) + if whenalways != '': + job.newPostscript(argv=whenalways, when="always", service=service) + + after = self.tractor_cfg.get('after', get_pref('rman_tractor_after')) + if after != '': + try: + aftersplit = after.split(' ') + if len(aftersplit) == 2: + t_date = aftersplit[0].split('/') + t_time = aftersplit[1].split(':') + if len(t_date) == 2 and len(t_time) == 2: + today = datetime.datetime.today() + job.after = datetime.datetime(today.year, int(t_date[0]), + int(t_date[1]), + int(t_time[0]), + int(t_time[1]), 0) + else: + print('Could not parse after date: %s. Ignoring.' % after) + else: + print('Could not parse after date: %s. Ignoring.' % after) + except: + print('Could not parse after date: %s. Ignoring.' % after) def _add_additional_prman_args(self, args): rm = self.bl_scene.renderman @@ -101,7 +156,9 @@ def add_blender_render_task(self, frame, parentTask, title, bl_filename, chunk=N task.title = title command = author.Command(local=False, service="PixarRender") - bl_blender_path = bpy.app.binary_path + bl_blender_path = 'blender' + if self.is_localqueue: + bl_blender_path = bpy.app.binary_path command.argv = [bl_blender_path] command.argv.append('-b') @@ -657,14 +714,15 @@ def spool(self, job, jobfile): subprocess.Popen(args, env=env) else: # spool to tractor - tractor_engine ='tractor-engine' - tractor_port = '80' + tractor_engine = get_pref("rman_tractor_hostname") + tractor_port = str(get_pref("rman_tractor_port")) owner = getpass.getuser() + if not get_pref("rman_tractor_local_user"): + owner = get_pref("rman_tractor_user") - tractor_cfg = rfb_config['tractor_cfg'] - tractor_engine = tractor_cfg.get('engine', tractor_engine) - tractor_port = str(tractor_cfg.get('port', tractor_port)) - owner = tractor_cfg.get('user', owner) + tractor_engine = self.tractor_cfg.get('engine', tractor_engine) + tractor_port = str(self.tractor_cfg.get('port', tractor_port)) + owner = self.tractor_cfg.get('user', owner) # env var trumps rfb.json tractor_env = envconfig().getenv('TRACTOR_ENGINE') From 4b3de679c1ee5964a1861ef0f5bbc40767457e41 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 4 Apr 2023 16:23:54 -0700 Subject: [PATCH 268/278] More fixes for tractor spooling: * for now, don't include the prman env key with the job. * don't load any of the Qt apps if we're in blender batch mode. We probably should go back to having the Qt apps be in their own separate files. --- rfb_utils/prefs_utils.py | 4 + .../rman_operators_editors_lightlink.py | 701 +++++----- .../rman_operators_editors_lightmixer.py | 1161 +++++++++-------- .../rman_operators_editors_tracegroups.py | 661 +++++----- .../rman_operators_editors_vol_aggregates.py | 604 ++++----- rman_presets/__init__.py | 24 +- rman_spool.py | 2 - rman_stats/operators.py | 129 +- rman_ui/rman_ui_txmanager.py | 102 +- 9 files changed, 1703 insertions(+), 1685 deletions(-) diff --git a/rfb_utils/prefs_utils.py b/rfb_utils/prefs_utils.py index 1b2a6df3..5f698504 100644 --- a/rfb_utils/prefs_utils.py +++ b/rfb_utils/prefs_utils.py @@ -13,9 +13,13 @@ def get_addon_prefs(): return None def using_qt(): + if bpy.app.background: + return False return get_pref('rman_ui_framework') == 'QT' def show_wip_qt(): + if bpy.app.background: + return False return get_pref('rman_show_wip_qt') def get_pref(pref_name='', default=None): diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py index 04be8b37..738f7aeb 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightlink.py @@ -11,373 +11,374 @@ from ... import rfb_icons from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config -from ...rman_ui import rfb_qt as rfb_qt from ...rman_constants import RFB_ADDON_PATH import bpy import os import re import sys -from PySide2 import QtCore, QtWidgets, QtGui __LIGHT_LINKING_WINDOW__ = None - -class LightLinkingQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.light_linking_qt_app_timed" - bl_label = "Light Linking Editor" - - def __init__(self): - super(LightLinkingQtAppTimed, self).__init__() - - def execute(self, context): - self._window = LightLinkingQtWrapper() - return super(LightLinkingQtAppTimed, self).execute(context) - -class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - -class LightLinkingQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(LightLinkingQtWrapper, self).__init__() - self.setObjectName("Dialog") - self.resize(825, 526) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) - - # hide OK, and cancel buttons - #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) +if not bpy.app.background: + from ...rman_ui import rfb_qt as rfb_qt + from PySide2 import QtCore, QtWidgets, QtGui + + class LightLinkingQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.light_linking_qt_app_timed" + bl_label = "Light Linking Editor" + + def __init__(self): + super(LightLinkingQtAppTimed, self).__init__() + + def execute(self, context): + self._window = LightLinkingQtWrapper() + return super(LightLinkingQtAppTimed, self).execute(context) + + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + + class LightLinkingQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(LightLinkingQtWrapper, self).__init__() + self.setObjectName("Dialog") + self.resize(825, 526) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(620, 450, 166, 24)) + + # hide OK, and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + #self.invert_checkBox = QtWidgets.QCheckBox(self) + #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) + #self.invert_checkBox.setObjectName("invert_checkBox") + self.widget = QtWidgets.QWidget(self) + self.widget.setGeometry(QtCore.QRect(40, 70, 751, 361)) + self.widget.setObjectName("widget") + self.gridLayout = QtWidgets.QGridLayout(self.widget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setHorizontalSpacing(50) + self.gridLayout.setObjectName("gridLayout") + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.lights_label = QtWidgets.QLabel(self.widget) + self.lights_label.setObjectName("lights_label") + self.verticalLayout.addWidget(self.lights_label) + self.lights_treeView = QtWidgets.QListWidget(self) + self.lights_treeView.setObjectName("lights_treeView") + self.verticalLayout.addWidget(self.lights_treeView) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.objects_label = QtWidgets.QLabel(self.widget) + self.objects_label.setObjectName("objects_label") + self.verticalLayout_2.addWidget(self.objects_label) + + self.objects_treeView = QtWidgets.QTreeView(self.widget) + self.objects_treeView.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + self.objects_treeView.setObjectName("objects_treeView") + self.objects_treeView.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.objects_treeView.setModel(self.treeModel) + + self.verticalLayout_2.addWidget(self.objects_treeView) + self.gridLayout.addLayout(self.verticalLayout_2, 0, 1, 1, 1) + + self.lights_treeView.itemSelectionChanged.connect(self.lights_index_changed) + + self.objects_treeView.selectionModel().selectionChanged.connect(self.linked_objects_selection) + + self.light_link_item = None + self.total_objects = 0 + self.retranslateUi() + self.refresh_lights() + + self.add_handlers() + + + def retranslateUi(self): + _translate = QtCore.QCoreApplication.translate + self.setWindowTitle(_translate("Dialog", "Light Linking")) + #self.invert_checkBox.setToolTip(_translate("Dialog", "Invert light linking")) + #self.invert_checkBox.setText(_translate("Dialog", "Invert")) + self.lights_label.setText(_translate("Dialog", "Lights")) + self.objects_label.setText(_translate("Dialog", "Objects")) + + def closeEvent(self, event): + self.remove_handlers() + super(LightLinkingQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.refresh_lights() + self.refresh_linked_objects() + #self.lights_index_changed() + #elif isinstance(dps_update.id, bpy.types.Scene): + # self.refresh_lights() - self.buttonBox.setObjectName("buttonBox") - #self.invert_checkBox = QtWidgets.QCheckBox(self) - #self.invert_checkBox.setGeometry(QtCore.QRect(730, 30, 85, 21)) - #self.invert_checkBox.setObjectName("invert_checkBox") - self.widget = QtWidgets.QWidget(self) - self.widget.setGeometry(QtCore.QRect(40, 70, 751, 361)) - self.widget.setObjectName("widget") - self.gridLayout = QtWidgets.QGridLayout(self.widget) - self.gridLayout.setContentsMargins(0, 0, 0, 0) - self.gridLayout.setHorizontalSpacing(50) - self.gridLayout.setObjectName("gridLayout") - self.verticalLayout = QtWidgets.QVBoxLayout() - self.verticalLayout.setObjectName("verticalLayout") - self.lights_label = QtWidgets.QLabel(self.widget) - self.lights_label.setObjectName("lights_label") - self.verticalLayout.addWidget(self.lights_label) - self.lights_treeView = QtWidgets.QListWidget(self) - self.lights_treeView.setObjectName("lights_treeView") - self.verticalLayout.addWidget(self.lights_treeView) - self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.objects_label = QtWidgets.QLabel(self.widget) - self.objects_label.setObjectName("objects_label") - self.verticalLayout_2.addWidget(self.objects_label) - - self.objects_treeView = QtWidgets.QTreeView(self.widget) - self.objects_treeView.setSelectionMode( - QtWidgets.QAbstractItemView.MultiSelection - ) - self.objects_treeView.setObjectName("objects_treeView") - self.objects_treeView.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.objects_treeView.setModel(self.treeModel) - - self.verticalLayout_2.addWidget(self.objects_treeView) - self.gridLayout.addLayout(self.verticalLayout_2, 0, 1, 1, 1) - - self.lights_treeView.itemSelectionChanged.connect(self.lights_index_changed) - - self.objects_treeView.selectionModel().selectionChanged.connect(self.linked_objects_selection) - - self.light_link_item = None - self.total_objects = 0 - self.retranslateUi() - self.refresh_lights() - - self.add_handlers() - - - def retranslateUi(self): - _translate = QtCore.QCoreApplication.translate - self.setWindowTitle(_translate("Dialog", "Light Linking")) - #self.invert_checkBox.setToolTip(_translate("Dialog", "Invert light linking")) - #self.invert_checkBox.setText(_translate("Dialog", "Invert")) - self.lights_label.setText(_translate("Dialog", "Lights")) - self.objects_label.setText(_translate("Dialog", "Objects")) - - def closeEvent(self, event): - self.remove_handlers() - super(LightLinkingQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Collection): - self.refresh_lights() - self.refresh_linked_objects() - #self.lights_index_changed() - #elif isinstance(dps_update.id, bpy.types.Scene): - # self.refresh_lights() - - def update(self): - super(LightLinkingQtWrapper, self).update() + def update(self): + super(LightLinkingQtWrapper, self).update() - def refresh_lights(self): - context = bpy.context - scene = context.scene - rm = scene.renderman + def refresh_lights(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + all_lights = [l.name for l in scene_utils.get_all_lights(scene, include_light_filters=True)] + remove_items = [] + + for i in range(self.lights_treeView.count()): + item = self.lights_treeView.item(i) + name = item.text() + if name not in all_lights: + remove_items.append(item) + else: + all_lights.remove(name) + + for nm in all_lights: + item = QtWidgets.QListWidgetItem(nm) + ob = scene.objects[nm] + light_shader_name = ob.data.renderman.get_light_node_name() + icon_path = os.path.join(RFB_ADDON_PATH, 'rfb_icons/out_%s.png' % light_shader_name) + if os.path.exists(icon_path): + icon = QtGui.QIcon(icon_path) + item.setIcon(icon) + item.setFlags(item.flags()) + self.lights_treeView.addItem(item) + + for item in remove_items: + self.lights_treeView.takeItem(self.lights_treeView.row(item)) + del item + + if remove_items or len(all_lights) > 0: + self.lights_treeView.setCurrentRow(-1) + + def find_light_link_item(self, light_nm=''): + context = bpy.context + scene = context.scene + rm = scene.renderman + light_link_item = None + for ll in rm.light_links: + if ll.light_ob.name == light_nm: + light_link_item = ll + break + return light_link_item + + def get_all_object_items(self, standard_item, items): + for i in range(standard_item.rowCount()): + item = standard_item.child(i) + items.append(item) + if item.rowCount() > 0: + self.get_all_object_items(item, items) + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + + def lights_index_changed(self): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + self.treeModel.clear() + self.objects_treeView.selectionModel().select(QtCore.QItemSelection(), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + return + self.rootNode = self.treeModel.invisibleRootItem() + if self.rootNode.rowCount() == 0: + self.refresh_linked_objects() + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.light_links_index = idx + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + if light_ob: + self.bl_select_objects([light_ob]) + + light_link_item = self.find_light_link_item(light_nm) + selected_items = QtCore.QItemSelection() + items = [] + self.get_all_object_items(self.rootNode, items) + + if light_link_item is None: + ''' + if not object_utils.is_light_filter(light_ob): + light_link_item = scene.renderman.light_links.add() + light_link_item.name = light_ob.name + light_link_item.light_ob = light_ob + ''' - all_lights = [l.name for l in scene_utils.get_all_lights(scene, include_light_filters=True)] - remove_items = [] - - for i in range(self.lights_treeView.count()): - item = self.lights_treeView.item(i) - name = item.text() - if name not in all_lights: - remove_items.append(item) - else: - all_lights.remove(name) - - for nm in all_lights: - item = QtWidgets.QListWidgetItem(nm) - ob = scene.objects[nm] - light_shader_name = ob.data.renderman.get_light_node_name() - icon_path = os.path.join(RFB_ADDON_PATH, 'rfb_icons/out_%s.png' % light_shader_name) - if os.path.exists(icon_path): - icon = QtGui.QIcon(icon_path) - item.setIcon(icon) - item.setFlags(item.flags()) - self.lights_treeView.addItem(item) - - for item in remove_items: - self.lights_treeView.takeItem(self.lights_treeView.row(item)) - del item - - if remove_items or len(all_lights) > 0: - self.lights_treeView.setCurrentRow(-1) - - def find_light_link_item(self, light_nm=''): - context = bpy.context - scene = context.scene - rm = scene.renderman - light_link_item = None - for ll in rm.light_links: - if ll.light_ob.name == light_nm: - light_link_item = ll - break - return light_link_item - - def get_all_object_items(self, standard_item, items): - for i in range(standard_item.rowCount()): - item = standard_item.child(i) - items.append(item) - if item.rowCount() > 0: - self.get_all_object_items(item, items) - - def bl_select_objects(self, obs): - context = bpy.context - for ob in context.selected_objects: - ob.select_set(False) - for ob in obs: - ob.select_set(True) - context.view_layer.objects.active = ob - - def lights_index_changed(self): - idx = int(self.lights_treeView.currentRow()) - current_item = self.lights_treeView.currentItem() - if not current_item: - self.treeModel.clear() - self.objects_treeView.selectionModel().select(QtCore.QItemSelection(), QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - return - self.rootNode = self.treeModel.invisibleRootItem() - if self.rootNode.rowCount() == 0: - self.refresh_linked_objects() - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.light_links_index = idx - light_nm = current_item.text() - light_ob = context.scene.objects.get(light_nm, None) - if light_ob: - self.bl_select_objects([light_ob]) - - light_link_item = self.find_light_link_item(light_nm) - selected_items = QtCore.QItemSelection() - items = [] - self.get_all_object_items(self.rootNode, items) - - if light_link_item is None: - ''' - if not object_utils.is_light_filter(light_ob): - light_link_item = scene.renderman.light_links.add() - light_link_item.name = light_ob.name - light_link_item.light_ob = light_ob - ''' - - for item in items: - idx = self.treeModel.indexFromItem(item) - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - else: - for item in items: - idx = self.treeModel.indexFromItem(item) - ob_nm = item.text() - found = False - for member in light_link_item.members: - ob = member.ob_pointer - if ob is None: + for item in items: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + else: + for item in items: + idx = self.treeModel.indexFromItem(item) + ob_nm = item.text() + found = False + for member in light_link_item.members: + ob = member.ob_pointer + if ob is None: + continue + if ob.name == ob_nm: + found = True + break + + if found: continue - if ob.name == ob_nm: - found = True - break - - if found: - continue - - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - self.objects_treeView.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - - def find_item(self, standard_item, ob): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if not item: - continue - if item.text() == ob.name: - return item - if item.rowCount() > 0: - return self.find_item(item, ob) - - return None - - def get_all_removed_items(self, standard_item, scene, remove_items): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if not item: - continue - nm = item.text() - if nm not in scene.objects: - remove_items.append(item) - if item.rowCount() > 0: - self.get_all_removed_items(item, scene, remove_items) - - def refresh_linked_objects(self): - context = bpy.context - scene = context.scene - self.rootNode = self.treeModel.invisibleRootItem() + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + self.objects_treeView.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - def add_children(root_item, ob): - for child in ob.children: - if child.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if not item: continue - item = self.find_item(root_item, child) + if item.text() == ob.name: + return item + if item.rowCount() > 0: + return self.find_item(item, ob) + + return None + + def get_all_removed_items(self, standard_item, scene, remove_items): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) if not item: - item = StandardItem(txt=child.name) - self.total_objects += 1 - root_item.appendRow(item) - if len(child.children) > 0: - add_children(item, child) - - remove_items = [] - self.get_all_removed_items(self.rootNode, scene, remove_items) - - for item in remove_items: - self.treeModel.takeRow(item.row()) - self.total_objects -= 1 - del item - - root_parents = [ob for ob in scene.objects if ob.parent is None] - for ob in root_parents: - if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: - continue - item = self.find_item(self.rootNode, ob) - if not item: - item = StandardItem(txt=ob.name) - self.total_objects += 1 - self.rootNode.appendRow(item) - if len(ob.children) > 0: - add_children(item, ob) - - self.objects_treeView.expandAll() - - def linked_objects_selection(self, selected, deselected): - idx = int(self.lights_treeView.currentRow()) - current_item = self.lights_treeView.currentItem() - if not current_item: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - light_nm = current_item.text() - light_ob = context.scene.objects.get(light_nm, None) - light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) - is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' - ll = self.find_light_link_item(light_nm) - - if ll is None: - ll = scene.renderman.light_links.add() - ll.name = light_ob.name - ll.light_ob = light_ob - rm.lights_link_index = len(rm.light_links)-1 - - if is_light_filter: - # linkingGroups should only be set if one of the items is deselected - total_selected_items = len(self.objects_treeView.selectionModel().selectedIndexes()) + continue + nm = item.text() + if nm not in scene.objects: + remove_items.append(item) + if item.rowCount() > 0: + self.get_all_removed_items(item, scene, remove_items) + - if total_selected_items == self.total_objects and light_props.linkingGroups != "": - light_props.linkingGroups = "" - light_ob.update_tag(refresh={'DATA'}) - elif total_selected_items != self.total_objects and light_props.linkingGroups == "": - light_props.linkingGroups = string_utils.sanitize_node_name(light_ob.name_full) - light_ob.update_tag(refresh={'DATA'}) - - for i in deselected.indexes(): - item = self.objects_treeView.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_add = True - for member in ll.members: - if ob == member.ob_pointer: - do_add = False - break - - if do_add: - member = ll.members.add() - member.name = ob.name - member.ob_pointer = ob - member.illuminate = 'OFF' - scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) - - for i in selected.indexes(): - item = self.objects_treeView.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_remove = False - idx = -1 - for i, member in enumerate(ll.members): - if ob == member.ob_pointer: - do_remove = True - idx = i - break - if do_remove: - member = ll.members.remove(idx) - scene_utils.set_lightlinking_properties(ob, light_ob, '') + def refresh_linked_objects(self): + context = bpy.context + scene = context.scene + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: + if child.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + continue + item = self.find_item(root_item, child) + if not item: + item = StandardItem(txt=child.name) + self.total_objects += 1 + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + remove_items = [] + self.get_all_removed_items(self.rootNode, scene, remove_items) + + for item in remove_items: + self.treeModel.takeRow(item.row()) + self.total_objects -= 1 + del item + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: + if ob.type in ['CAMERA', 'LIGHT', 'ARMATURE']: + continue + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.total_objects += 1 + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.objects_treeView.expandAll() + + def linked_objects_selection(self, selected, deselected): + idx = int(self.lights_treeView.currentRow()) + current_item = self.lights_treeView.currentItem() + if not current_item: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + light_nm = current_item.text() + light_ob = context.scene.objects.get(light_nm, None) + light_props = shadergraph_utils.get_rman_light_properties_group(light_ob.original) + is_light_filter = light_props.renderman_light_role == 'RMAN_LIGHTFILTER' + ll = self.find_light_link_item(light_nm) + + if ll is None: + ll = scene.renderman.light_links.add() + ll.name = light_ob.name + ll.light_ob = light_ob + rm.lights_link_index = len(rm.light_links)-1 + + if is_light_filter: + # linkingGroups should only be set if one of the items is deselected + total_selected_items = len(self.objects_treeView.selectionModel().selectedIndexes()) + + if total_selected_items == self.total_objects and light_props.linkingGroups != "": + light_props.linkingGroups = "" + light_ob.update_tag(refresh={'DATA'}) + elif total_selected_items != self.total_objects and light_props.linkingGroups == "": + light_props.linkingGroups = string_utils.sanitize_node_name(light_ob.name_full) + light_ob.update_tag(refresh={'DATA'}) + + for i in deselected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in ll.members: + if ob == member.ob_pointer: + do_add = False + break + + if do_add: + member = ll.members.add() + member.name = ob.name + member.ob_pointer = ob + member.illuminate = 'OFF' + scene_utils.set_lightlinking_properties(ob, light_ob, member.illuminate) + + for i in selected.indexes(): + item = self.objects_treeView.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_remove = False + idx = -1 + for i, member in enumerate(ll.members): + if ob == member.ob_pointer: + do_remove = True + idx = i + break + if do_remove: + member = ll.members.remove(idx) + scene_utils.set_lightlinking_properties(ob, light_ob, '') class RENDERMAN_UL_LightLink_Light_List(bpy.types.UIList): @@ -687,9 +688,11 @@ def invoke(self, context, event): PRMAN_PT_Renderman_Open_Light_Linking, RENDERMAN_UL_LightLink_Light_List, RENDERMAN_UL_LightLink_Object_List, - LightLinkingQtAppTimed ] +if not bpy.app.background: + classes.append(LightLinkingQtAppTimed) + def register(): from ...rfb_utils import register_utils diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py index 018ab2d1..38dcc639 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py @@ -6,583 +6,532 @@ from ...rfb_logger import rfb_log from ... import rfb_icons from ...rfb_utils.prefs_utils import using_qt, show_wip_qt -from ...rman_ui import rfb_qt as rfb_qt from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config import bpy import re import sys -from PySide2 import QtCore, QtWidgets, QtGui __LIGHT_MIXER_WINDOW__ = None -class LightMixerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.light_mixer_qt_app_timed" - bl_label = "RenderMan Trace Sets Editor" - - def __init__(self): - super(LightMixerQtAppTimed, self).__init__() - - def execute(self, context): - self._window = LightMixerQtWrapper() - return super(LightMixerQtAppTimed, self).execute(context) - -class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - -class ParamLabel(QtWidgets.QLabel): - def __init__(self, *args, **kwargs): - super(ParamLabel, self).__init__(*args, **kwargs) - #self.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - -class VBoxLayout(QtWidgets.QVBoxLayout): - def __init__(self, m=1, s=1): - super(VBoxLayout, self).__init__() - self.setContentsMargins(m, m, m, m) - self.setSpacing(s) - - -class HBoxLayout(QtWidgets.QHBoxLayout): - def __init__(self, m=5, s=10): - super(HBoxLayout, self).__init__() - self.setContentsMargins(m, m, m, m) - self.setSpacing(s) - -class SliderParam(QtWidgets.QWidget): - def __init__(self, *args, **kwargs): - super(SliderParam, self).__init__(parent=kwargs.get("parent", None)) - self.light_shader = None - - # build ui - self._lyt = HBoxLayout() - self.light_shader = kwargs.get("light_shader", None) - self.param = kwargs.get("param", "") - self.param_label = ParamLabel(kwargs.get("label", "label")) - self._lyt.addWidget(self.param_label) - self.sl = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) - self.sl.setMinimum(kwargs.get("min", 0.0)) - self.sl.setMaximum(kwargs.get("max", 10.0)) - self.sl.setValue(kwargs.get("value", 0.0)) - self.sl.setTickPosition(QtWidgets.QSlider.TicksBothSides) - self.sl.setTickInterval(1.0) - self.sl.setSingleStep(kwargs.get("step", 0.1)) - self.sl.valueChanged.connect(self.slider_changed) - self._lyt.addWidget(self.sl) - - self._field = QtWidgets.QDoubleSpinBox() - self._field.setMinimum(0.0) - self._field.setMaximum(100000.0) - self._field.setSingleStep(kwargs.get("step", 0.1)) - self._field.setValue(kwargs.get("value", 0.0)) - self._field.valueChanged.connect(self.value_changed) - #self.setToolTip(kwargs.get("tooltip", "")) - self._lyt.addWidget(self._field) - self.setLayout(self._lyt) - - def value_changed(self, val): - val = self._field.value() - #self.sl.setValue(val) - self.update_shader(val) - - def slider_changed(self): - val = self.sl.value() - self._field.setValue(val) - self.update_shader(val) - - def update_shader(self, val): - if self.light_shader: - setattr(self.light_shader, self.param, val) - -class FloatParam(QtWidgets.QWidget): - def __init__(self, *args, **kwargs): - super(FloatParam, self).__init__(parent=kwargs.get("parent", None)) - # build ui - self._lyt = HBoxLayout() - self.light_shader = kwargs.get("light_shader", None) - self.param = kwargs.get("param", "") - self.param_label = ParamLabel(kwargs.get("label", "label")) - self._lyt.addWidget(self.param_label) - self._field = QtWidgets.QDoubleSpinBox() - self._field.setMinimum(kwargs.get("min", 0.0)) - self._field.setMaximum(kwargs.get("max", 1.0)) - self._field.setSingleStep(kwargs.get("step", 0.1)) - self._field.setValue(kwargs.get("value", 0.0)) - self.setToolTip(kwargs.get("tooltip", "")) - self._lyt.addWidget(self._field) - self.setLayout(self._lyt) - # change cb - self._field.valueChanged.connect(self.on_change) - - @property - def value(self): - return self._field.value - - def on_change(self, val): - setattr(self.light_shader, self.param, val) - -class BoolParam(QtWidgets.QWidget): - def __init__(self, *args, **kwargs): - super(BoolParam, self).__init__(parent=kwargs.get("parent", None)) - self.light_shader = kwargs.get("light_shader", None) - self.param = kwargs.get("param", "") - self.param_label = ParamLabel(kwargs.get("label", "label")) - self._lyt = HBoxLayout(m=1, s=1) - self._lyt.addWidget(self.param_label) - self._cb = QtWidgets.QCheckBox() - self._cb.setTristate(False) - self._cb.setChecked(kwargs.get("value", False)) - self.setToolTip(kwargs.get("tooltip", "")) - self._lyt.addWidget(self._cb) - self.setLayout(self._lyt) - # change cb - self._cb.stateChanged.connect(self.on_change) - - @property - def value(self): - return self._cb.isChecked() - - def setChecked(self, v): - self._cb.setChecked(v) - - def on_change(self, val): - setattr(self.light_shader, self.param, bool(val)) - -class ColorButton(QtWidgets.QWidget): - colorChanged = QtCore.Signal(object) - - def __init__(self, *args, **kwargs): - super(ColorButton, self).__init__(parent=kwargs.get("parent", None)) - - self._lyt = HBoxLayout() - self.light_shader = kwargs.get("light_shader", None) - self.param = kwargs.get("param", "") - self.param_label = ParamLabel(kwargs.get("label", "label")) - self._lyt.addWidget(self.param_label) - self._color = None - clr = kwargs.get("color", (0.0, 0.0, 0.0)) - self._default = QtGui.QColor.fromRgbF(clr[0], clr[1], clr[2]) - self.color_btn = QtWidgets.QPushButton() - self.color_btn.pressed.connect(self.onColorPicker) - self._lyt.addWidget(self.color_btn) - self.dlg = None - - self.setLayout(self._lyt) - - self._color = self._default - self.color_btn.setStyleSheet("background-color: %s;" % self._color.name()) - - def setColor(self, qcolor): - if qcolor != self._color: - self._color = qcolor - self.colorChanged.emit(qcolor) - - if self._color: - self.color_btn.setStyleSheet("background-color: %s;" % qcolor.name()) - else: - self.color_btn.setStyleSheet("") +if not bpy.app.background: + from ...rman_ui import rfb_qt as rfb_qt + from PySide2 import QtCore, QtWidgets, QtGui + + class LightMixerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.light_mixer_qt_app_timed" + bl_label = "RenderMan Trace Sets Editor" + + def __init__(self): + super(LightMixerQtAppTimed, self).__init__() + + def execute(self, context): + self._window = LightMixerQtWrapper() + return super(LightMixerQtAppTimed, self).execute(context) + + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) + + class ParamLabel(QtWidgets.QLabel): + def __init__(self, *args, **kwargs): + super(ParamLabel, self).__init__(*args, **kwargs) + #self.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + + class VBoxLayout(QtWidgets.QVBoxLayout): + def __init__(self, m=1, s=1): + super(VBoxLayout, self).__init__() + self.setContentsMargins(m, m, m, m) + self.setSpacing(s) + + + class HBoxLayout(QtWidgets.QHBoxLayout): + def __init__(self, m=5, s=10): + super(HBoxLayout, self).__init__() + self.setContentsMargins(m, m, m, m) + self.setSpacing(s) + + class SliderParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(SliderParam, self).__init__(parent=kwargs.get("parent", None)) + self.light_shader = None + + # build ui + self._lyt = HBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self.sl = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) + self.sl.setMinimum(kwargs.get("min", 0.0)) + self.sl.setMaximum(kwargs.get("max", 10.0)) + self.sl.setValue(kwargs.get("value", 0.0)) + self.sl.setTickPosition(QtWidgets.QSlider.TicksBothSides) + self.sl.setTickInterval(1.0) + self.sl.setSingleStep(kwargs.get("step", 0.1)) + self.sl.valueChanged.connect(self.slider_changed) + self._lyt.addWidget(self.sl) + + self._field = QtWidgets.QDoubleSpinBox() + self._field.setMinimum(0.0) + self._field.setMaximum(100000.0) + self._field.setSingleStep(kwargs.get("step", 0.1)) + self._field.setValue(kwargs.get("value", 0.0)) + self._field.valueChanged.connect(self.value_changed) + #self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._field) + self.setLayout(self._lyt) + + def value_changed(self, val): + val = self._field.value() + #self.sl.setValue(val) + self.update_shader(val) + + def slider_changed(self): + val = self.sl.value() + self._field.setValue(val) + self.update_shader(val) + + def update_shader(self, val): + if self.light_shader: + setattr(self.light_shader, self.param, val) + + class FloatParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(FloatParam, self).__init__(parent=kwargs.get("parent", None)) + # build ui + self._lyt = HBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self._field = QtWidgets.QDoubleSpinBox() + self._field.setMinimum(kwargs.get("min", 0.0)) + self._field.setMaximum(kwargs.get("max", 1.0)) + self._field.setSingleStep(kwargs.get("step", 0.1)) + self._field.setValue(kwargs.get("value", 0.0)) + self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._field) + self.setLayout(self._lyt) + # change cb + self._field.valueChanged.connect(self.on_change) + + @property + def value(self): + return self._field.value + + def on_change(self, val): + setattr(self.light_shader, self.param, val) + + class BoolParam(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(BoolParam, self).__init__(parent=kwargs.get("parent", None)) + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt = HBoxLayout(m=1, s=1) + self._lyt.addWidget(self.param_label) + self._cb = QtWidgets.QCheckBox() + self._cb.setTristate(False) + self._cb.setChecked(kwargs.get("value", False)) + self.setToolTip(kwargs.get("tooltip", "")) + self._lyt.addWidget(self._cb) + self.setLayout(self._lyt) + # change cb + self._cb.stateChanged.connect(self.on_change) + + @property + def value(self): + return self._cb.isChecked() + + def setChecked(self, v): + self._cb.setChecked(v) + + def on_change(self, val): + setattr(self.light_shader, self.param, bool(val)) + + class ColorButton(QtWidgets.QWidget): + colorChanged = QtCore.Signal(object) + + def __init__(self, *args, **kwargs): + super(ColorButton, self).__init__(parent=kwargs.get("parent", None)) + + self._lyt = HBoxLayout() + self.light_shader = kwargs.get("light_shader", None) + self.param = kwargs.get("param", "") + self.param_label = ParamLabel(kwargs.get("label", "label")) + self._lyt.addWidget(self.param_label) + self._color = None + clr = kwargs.get("color", (0.0, 0.0, 0.0)) + self._default = QtGui.QColor.fromRgbF(clr[0], clr[1], clr[2]) + self.color_btn = QtWidgets.QPushButton() + self.color_btn.pressed.connect(self.onColorPicker) + self._lyt.addWidget(self.color_btn) + self.dlg = None + + self.setLayout(self._lyt) + + self._color = self._default + self.color_btn.setStyleSheet("background-color: %s;" % self._color.name()) + + def setColor(self, qcolor): + if qcolor != self._color: + self._color = qcolor + self.colorChanged.emit(qcolor) + + if self._color: + self.color_btn.setStyleSheet("background-color: %s;" % qcolor.name()) + else: + self.color_btn.setStyleSheet("") - if self.light_shader: - setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) + if self.light_shader: + setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) - def color(self): - return self._color + def color(self): + return self._color - def onColorPicker(self): - if self.dlg is None: - self.dlg = QtWidgets.QColorDialog(self) - self.dlg.setOption(QtWidgets.QColorDialog.DontUseNativeDialog) - self.dlg.currentColorChanged.connect(self.currentColorChanged) - self.dlg.accepted.connect(self.dlg_accept) - self.dlg.rejected.connect(self.dlg_rejected) + def onColorPicker(self): + if self.dlg is None: + self.dlg = QtWidgets.QColorDialog(self) + self.dlg.setOption(QtWidgets.QColorDialog.DontUseNativeDialog) + self.dlg.currentColorChanged.connect(self.currentColorChanged) + self.dlg.accepted.connect(self.dlg_accept) + self.dlg.rejected.connect(self.dlg_rejected) - self.dlg.setCurrentColor(self._color) - self.dlg.open() - - def dlg_accept(self): - self.setColor(self.dlg.currentColor()) + self.dlg.setCurrentColor(self._color) + self.dlg.open() + + def dlg_accept(self): + self.setColor(self.dlg.currentColor()) - def dlg_rejected(self): - self.setColor(self._color) + def dlg_rejected(self): + self.setColor(self._color) - def currentColorChanged(self, qcolor): - if self.light_shader: - setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) + def currentColorChanged(self, qcolor): + if self.light_shader: + setattr(self.light_shader, self.param, (qcolor.redF(), qcolor.greenF(), qcolor.blueF())) - def mousePressEvent(self, e): - if e.button() == QtCore.Qt.RightButton: - self.setColor(self._default) + def mousePressEvent(self, e): + if e.button() == QtCore.Qt.RightButton: + self.setColor(self._default) - return super(ColorButton, self).mousePressEvent(e) - -class LightMixerWidget(QtWidgets.QWidget): - def __init__(self, *args, **kwargs): - super(LightMixerWidget, self).__init__(parent=kwargs.get("parent", None)) - - self._lyt = VBoxLayout(m=5) - self._lyt.setAlignment(QtCore.Qt.AlignTop) - self._lbl = QtWidgets.QLabel() - self._lbl.setAlignment(QtCore.Qt.AlignHCenter) - lbl = kwargs.get("name", "Light") - self._lbl.setText(lbl) - self._lyt.addWidget(self._lbl) - self.setLayout(self._lyt) - - def __del__(self): - self.remove_widgets() - - def add_widget(self, wgt): - self._lyt.addWidget(wgt) - - def remove_widgets(self): - for i in reversed(range(self._lyt.count())): - w = self._lyt.takeAt(i).widget() - if w is not None: - w.setParent(None) - w.deleteLater() - -class LightMixerLayout(QtWidgets.QWidget): - def __init__(self, *args, **kwargs): - super(LightMixerLayout, self).__init__(parent=kwargs.get("parent", None)) - - self.groupbox = QtWidgets.QGroupBox('') - self._gb_lyt = VBoxLayout() - self.groupbox.setLayout(self._gb_lyt) - scroll = QtWidgets.QScrollArea() - scroll.setWidget(self.groupbox) - scroll.setWidgetResizable(True) - self._lyt = VBoxLayout() - self._lyt.addWidget(scroll) - self.setLayout(self._lyt) - - def add_widget(self, wgt): - self._gb_lyt.addWidget(wgt) - - def remove_widgets(self): - for i in reversed(range(self._gb_lyt.count())): - w = self._gb_lyt.takeAt(i).widget() - if w is not None: - w.setParent(None) - w.deleteLater() - -class LightMixerQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(LightMixerQtWrapper, self).__init__() - - self.setWindowTitle('RenderMan Light Mixer') - self.resize(1100, 500) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + return super(ColorButton, self).mousePressEvent(e) - # hide OK and cancel buttons - #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + class LightMixerWidget(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(LightMixerWidget, self).__init__(parent=kwargs.get("parent", None)) + + self._lyt = VBoxLayout(m=5) + self._lyt.setAlignment(QtCore.Qt.AlignTop) + self._lbl = QtWidgets.QLabel() + self._lbl.setAlignment(QtCore.Qt.AlignHCenter) + lbl = kwargs.get("name", "Light") + self._lbl.setText(lbl) + self._lyt.addWidget(self._lbl) + self.setLayout(self._lyt) + + def __del__(self): + self.remove_widgets() + + def add_widget(self, wgt): + self._lyt.addWidget(wgt) + + def remove_widgets(self): + for i in reversed(range(self._lyt.count())): + w = self._lyt.takeAt(i).widget() + if w is not None: + w.setParent(None) + w.deleteLater() + + class LightMixerLayout(QtWidgets.QWidget): + def __init__(self, *args, **kwargs): + super(LightMixerLayout, self).__init__(parent=kwargs.get("parent", None)) + + self.groupbox = QtWidgets.QGroupBox('') + self._gb_lyt = VBoxLayout() + self.groupbox.setLayout(self._gb_lyt) + scroll = QtWidgets.QScrollArea() + scroll.setWidget(self.groupbox) + scroll.setWidgetResizable(True) + self._lyt = VBoxLayout() + self._lyt.addWidget(scroll) + self.setLayout(self._lyt) + + def add_widget(self, wgt): + self._gb_lyt.addWidget(wgt) + + def remove_widgets(self): + for i in reversed(range(self._gb_lyt.count())): + w = self._gb_lyt.takeAt(i).widget() + if w is not None: + w.setParent(None) + w.deleteLater() + + class LightMixerQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(LightMixerQtWrapper, self).__init__() - self.buttonBox.setObjectName("buttonBox") - self.addButton = QtWidgets.QPushButton(self) - self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) - self.addButton.setObjectName("addButton") - self.addButton.setText("+") - self.addButton.setAutoDefault(False) - self.removeButton = QtWidgets.QPushButton(self) - self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) - self.removeButton.setObjectName("removeButton") - self.removeButton.setText("-") - self.removeButton.setAutoDefault(False) - - self.mixerGroupObjects = QtWidgets.QTreeView(self) - self.mixerGroupObjects.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.mixerGroupObjects.setModel(self.treeModel) - - self.mixerGroupObjects.setGeometry(QtCore.QRect(30, 140, 250, 350)) - self.mixerGroupObjects.setObjectName("mixerGroupObjects") - self.mixerGroupObjects.setSelectionMode( - QtWidgets.QAbstractItemView.SingleSelection - ) - - self.mixerGroups = QtWidgets.QListWidget(self) - self.mixerGroups.setGeometry(QtCore.QRect(30, 30, 256, 80)) - self.mixerGroups.setObjectName("mixerGroups") - - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 10, 200, 17)) - self.label.setText("Light Mixer Groups") - - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 120, 200, 17)) - self.label_2.setText("Lights") - - self.add_light_btn = QtWidgets.QPushButton(self) - self.add_light_btn.setGeometry(QtCore.QRect(279, 140, 31, 26)) - self.add_light_btn.setText("+") - self.add_light_btn.setToolTip("""Add selected lights to this mixer group""" ) - self.add_light_btn.setEnabled(False) - self.add_light_btn.setAutoDefault(False) - - self.remove_light_btn = QtWidgets.QPushButton(self) - self.remove_light_btn.setGeometry(QtCore.QRect(279, 160, 31, 26)) - self.remove_light_btn.setText("-") - self.remove_light_btn.setToolTip("""Remove selected lights""" ) - self.remove_light_btn.setEnabled(False) - self.remove_light_btn.setAutoDefault(False) - - self.addButton.clicked.connect(self.add_group) - self.removeButton.clicked.connect(self.remove_group) - self.add_light_btn.clicked.connect(self.add_light) - self.remove_light_btn.clicked.connect(self.remove_light) - - self.mixerGroups.itemChanged.connect(self.mixer_group_changed) - self.mixerGroups.itemSelectionChanged.connect(self.mixer_groups_index_changed) - self.mixerGroupObjects.selectionModel().selectionChanged.connect(self.mixer_group_objects_selection) - - self.light_mixer_wgt = LightMixerLayout(parent=self) - self.light_mixer_wgt.setGeometry(QtCore.QRect(340, 30, 600, 400)) - - self.refresh_groups() - self.mixerGroupObjects.expandAll() - self.enableAddLightButton() - - self.add_handlers() - - def closeEvent(self, event): - self.remove_handlers() - super(LightMixerQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Collection): - self.refresh_group_objects() - elif isinstance(dps_update.id, bpy.types.Scene): - self.enableAddLightButton() - - def enableAddLightButton(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - lights = [ob for ob in context.selected_objects if ob.type == "LIGHT"] - if not lights: - any_lights = [] + self.setWindowTitle('RenderMan Light Mixer') + self.resize(1100, 500) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.addButton.setAutoDefault(False) + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + self.removeButton.setAutoDefault(False) + + self.mixerGroupObjects = QtWidgets.QTreeView(self) + self.mixerGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.mixerGroupObjects.setModel(self.treeModel) + + self.mixerGroupObjects.setGeometry(QtCore.QRect(30, 140, 250, 350)) + self.mixerGroupObjects.setObjectName("mixerGroupObjects") + self.mixerGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.SingleSelection + ) + + self.mixerGroups = QtWidgets.QListWidget(self) + self.mixerGroups.setGeometry(QtCore.QRect(30, 30, 256, 80)) + self.mixerGroups.setObjectName("mixerGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 200, 17)) + self.label.setText("Light Mixer Groups") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 120, 200, 17)) + self.label_2.setText("Lights") + + self.add_light_btn = QtWidgets.QPushButton(self) + self.add_light_btn.setGeometry(QtCore.QRect(279, 140, 31, 26)) + self.add_light_btn.setText("+") + self.add_light_btn.setToolTip("""Add selected lights to this mixer group""" ) + self.add_light_btn.setEnabled(False) + self.add_light_btn.setAutoDefault(False) + + self.remove_light_btn = QtWidgets.QPushButton(self) + self.remove_light_btn.setGeometry(QtCore.QRect(279, 160, 31, 26)) + self.remove_light_btn.setText("-") + self.remove_light_btn.setToolTip("""Remove selected lights""" ) + self.remove_light_btn.setEnabled(False) + self.remove_light_btn.setAutoDefault(False) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.add_light_btn.clicked.connect(self.add_light) + self.remove_light_btn.clicked.connect(self.remove_light) + + self.mixerGroups.itemChanged.connect(self.mixer_group_changed) + self.mixerGroups.itemSelectionChanged.connect(self.mixer_groups_index_changed) + self.mixerGroupObjects.selectionModel().selectionChanged.connect(self.mixer_group_objects_selection) + + self.light_mixer_wgt = LightMixerLayout(parent=self) + self.light_mixer_wgt.setGeometry(QtCore.QRect(340, 30, 600, 400)) + + self.refresh_groups() + self.mixerGroupObjects.expandAll() + self.enableAddLightButton() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(LightMixerQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.refresh_group_objects() + elif isinstance(dps_update.id, bpy.types.Scene): + self.enableAddLightButton() + + def enableAddLightButton(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + lights = [ob for ob in context.selected_objects if ob.type == "LIGHT"] + if len(lights) > 0: + any_lights = [] + if rm.light_mixer_groups_index > 0: + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + for ob in lights: + do_add = True + for member in grp.members: + if member.light_ob == ob: + do_add = False + break + if do_add: + any_lights.append(ob) + + self.add_light_btn.setEnabled(len(any_lights) > 0) + return + self.add_light_btn.setEnabled(True) + return + self.add_light_btn.setEnabled(False) + + def add_light(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] - for ob in lights: + for ob in context.selected_objects: do_add = True for member in grp.members: if member.light_ob == ob: do_add = False break if do_add: - any_lights.append(ob) - - self.add_light_btn.setEnabled(len(any_lights) > 0) - return - self.add_light_btn.setEnabled(True) - - def add_light(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.light_mixer_groups[rm.light_mixer_groups_index] - for ob in context.selected_objects: - do_add = True - for member in grp.members: - if member.light_ob == ob: - do_add = False - break - if do_add: - ob_in_group = grp.members.add() - ob_in_group.name = ob.name - ob_in_group.light_ob = ob - - self.refresh_group_objects() - - def remove_light(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - grp = rm.light_mixer_groups[rm.light_mixer_groups_index] - - index = self.mixerGroupObjects.selectedIndexes()[0] - item = index.model().itemFromIndex(index) - light_nm = item.text() - ob = context.scene.objects.get(light_nm, None) - if not ob: - return - - do_remove = False - idx = -1 - for i, member in enumerate(grp.members): - if ob == member.light_ob: - do_remove = True - idx = i - break - if do_remove: - member = grp.members.remove(idx) - - self.refresh_group_objects() - - - def update(self): - idx = int(self.mixerGroups.currentRow()) - self.addButton.setEnabled(True) - - super(LightMixerQtWrapper, self).update() - - def refresh_groups(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - self.mixerGroups.clear() - for grp in rm.light_mixer_groups: - item = QtWidgets.QListWidgetItem(grp.name) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - self.mixerGroups.addItem(item) - - if self.mixerGroups.count() > 0: - self.mixerGroups.setCurrentRow(rm.light_mixer_groups_index) + ob_in_group = grp.members.add() + ob_in_group.name = ob.name + ob_in_group.light_ob = ob + + self.refresh_group_objects() + + def remove_light(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + + index = self.mixerGroupObjects.selectedIndexes()[0] + item = index.model().itemFromIndex(index) + light_nm = item.text() + ob = context.scene.objects.get(light_nm, None) + if not ob: + return + + do_remove = False + idx = -1 + for i, member in enumerate(grp.members): + if ob == member.light_ob: + do_remove = True + idx = i + break + if do_remove: + member = grp.members.remove(idx) + + self.refresh_group_objects() - def add_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.light_mixer_groups.add() - grp.name = 'mixerGroup_%d' % len(rm.light_mixer_groups) - rm.light_mixer_groups_index = len(rm.light_mixer_groups)-1 - self.refresh_groups() - def remove_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman + def update(self): + idx = int(self.mixerGroups.currentRow()) + self.addButton.setEnabled(True) - index = rm.object_groups_index - group = rm.object_groups[index] - # get a list of all objects in this group - ob_list = [member.ob_pointer for member in group.members] - rm.object_groups.remove(index) - rm.object_groups_index -= 1 + super(LightMixerQtWrapper, self).update() - # now tell each object to update - for ob in ob_list: - ob.update_tag(refresh={'OBJECT'}) + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.mixerGroups.clear() + for grp in rm.light_mixer_groups: + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.mixerGroups.addItem(item) - self.refresh_groups() + if self.mixerGroups.count() > 0: + self.mixerGroups.setCurrentRow(rm.light_mixer_groups_index) + + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.light_mixer_groups.add() + grp.name = 'mixerGroup_%d' % len(rm.light_mixer_groups) + rm.light_mixer_groups_index = len(rm.light_mixer_groups)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.object_groups_index + group = rm.object_groups[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.object_groups.remove(index) + rm.object_groups_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'OBJECT'}) + + self.refresh_groups() + + def mixer_group_changed(self, item): + idx = int(self.mixerGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.light_mixer_groups[idx] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.light_ob + ob.update_tag(refresh={'OBJECT'}) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + + return None + + def refresh_group_objects(self): + self.light_mixer_wgt.remove_widgets() + idx = int(self.mixerGroups.currentRow()) + if idx == -1: + self.label_2.setText("Objects (no group selected)") + - def mixer_group_changed(self, item): - idx = int(self.mixerGroups.currentRow()) + context = bpy.context + scene = context.scene + rm = scene.renderman - context = bpy.context - scene = context.scene - rm = scene.renderman - grp = rm.light_mixer_groups[idx] - grp.name = item.text() - self.label_2.setText("Objects (%s)" % item.text()) - for member in grp.members: - ob = member.light_ob - ob.update_tag(refresh={'OBJECT'}) - - def find_item(self, standard_item, ob): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - - return None - - def refresh_group_objects(self): - self.light_mixer_wgt.remove_widgets() - idx = int(self.mixerGroups.currentRow()) - if idx == -1: - self.label_2.setText("Objects (no group selected)") + grp = rm.light_mixer_groups[rm.light_mixer_groups_index] + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.light_mixer_groups[rm.light_mixer_groups_index] - - self.treeModel.clear() - self.rootNode = self.treeModel.invisibleRootItem() + for member in grp.members: + ob = member.light_ob + + light_shader = shadergraph_utils.get_light_node(ob) + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) - for member in grp.members: - ob = member.light_ob - - light_shader = shadergraph_utils.get_light_node(ob) - item = StandardItem(txt=ob.name) - self.rootNode.appendRow(item) - - lgt_mixer_wgt = LightMixerWidget(parent=self, name=ob.name) - - if light_shader.bl_label == 'PxrPortalLight': - enableTemperature = BoolParam(parent=self, - param="enableTemperature", - label="Enable Temperature", - value=light_shader.enableTemperature, - light_shader=light_shader - ) - lgt_mixer_wgt.add_widget(enableTemperature) - - temperature = FloatParam(parent=self, - param="temperature", - label="Temperature", - min=1000.0, - max=50000.0, - value=light_shader.temperature, - light_shader=light_shader - ) - lgt_mixer_wgt.add_widget(temperature) - - wgt = SliderParam(parent=self, - light_shader=light_shader, - value=light_shader.intensityMult, - min=0.0, - max=10.0, - param="intensityMult", - label="Intensity Mult") - lgt_mixer_wgt.add_widget(wgt) - + lgt_mixer_wgt = LightMixerWidget(parent=self, name=ob.name) - else: - exposure_wgt = SliderParam(parent=self, - light_shader=light_shader, - value=light_shader.exposure, - min=0.0, - max=10.0, - param="exposure", - label="Exposure") - lgt_mixer_wgt.add_widget(exposure_wgt) - - wgt = SliderParam(parent=self, - light_shader=light_shader, - value=light_shader.intensity, - min=0.0, - max=10.0, - param="intensity", - label="Intensity") - lgt_mixer_wgt.add_widget(wgt) - - if light_shader.bl_label == 'PxrEnvDayLight': - color_picker = ColorButton(parent=self, - color=light_shader.skyTint, - param="skyTint", - label="Sky Tint", - light_shader=light_shader - ) - lgt_mixer_wgt.add_widget(color_picker) - else: + if light_shader.bl_label == 'PxrPortalLight': enableTemperature = BoolParam(parent=self, param="enableTemperature", label="Enable Temperature", @@ -599,42 +548,98 @@ def refresh_group_objects(self): value=light_shader.temperature, light_shader=light_shader ) - lgt_mixer_wgt.add_widget(temperature) - - color_picker = ColorButton(parent=self, - color=light_shader.lightColor, - param="lightColor", - label="Light Color", - light_shader=light_shader - ) - - lgt_mixer_wgt.add_widget(color_picker) - - self.light_mixer_wgt.add_widget(lgt_mixer_wgt) - - self.mixerGroupObjects.expandAll() - - def mixer_groups_index_changed(self): - idx = int(self.mixerGroups.currentRow()) - current_item = self.mixerGroups.currentItem() - if current_item: - self.label_2.setText("Lights (%s)" % current_item.text()) - else: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.light_mixer_groups_index = idx + lgt_mixer_wgt.add_widget(temperature) + + wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.intensityMult, + min=0.0, + max=10.0, + param="intensityMult", + label="Intensity Mult") + lgt_mixer_wgt.add_widget(wgt) + - self.refresh_group_objects() + else: + exposure_wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.exposure, + min=0.0, + max=10.0, + param="exposure", + label="Exposure") + lgt_mixer_wgt.add_widget(exposure_wgt) - def mixer_group_objects_selection(self, selected, deselected): - idx = int(self.mixerGroups.currentRow()) - current_item = self.mixerGroups.currentItem() - if not current_item: - self.remove_light_btn.setEnabled(False) - return - self.remove_light_btn.setEnabled(True) + wgt = SliderParam(parent=self, + light_shader=light_shader, + value=light_shader.intensity, + min=0.0, + max=10.0, + param="intensity", + label="Intensity") + lgt_mixer_wgt.add_widget(wgt) + + if light_shader.bl_label == 'PxrEnvDayLight': + color_picker = ColorButton(parent=self, + color=light_shader.skyTint, + param="skyTint", + label="Sky Tint", + light_shader=light_shader + ) + lgt_mixer_wgt.add_widget(color_picker) + else: + enableTemperature = BoolParam(parent=self, + param="enableTemperature", + label="Enable Temperature", + value=light_shader.enableTemperature, + light_shader=light_shader + ) + lgt_mixer_wgt.add_widget(enableTemperature) + + temperature = FloatParam(parent=self, + param="temperature", + label="Temperature", + min=1000.0, + max=50000.0, + value=light_shader.temperature, + light_shader=light_shader + ) + lgt_mixer_wgt.add_widget(temperature) + + color_picker = ColorButton(parent=self, + color=light_shader.lightColor, + param="lightColor", + label="Light Color", + light_shader=light_shader + ) + + lgt_mixer_wgt.add_widget(color_picker) + + self.light_mixer_wgt.add_widget(lgt_mixer_wgt) + + self.mixerGroupObjects.expandAll() + + def mixer_groups_index_changed(self): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + if current_item: + self.label_2.setText("Lights (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.light_mixer_groups_index = idx + + self.refresh_group_objects() + + def mixer_group_objects_selection(self, selected, deselected): + idx = int(self.mixerGroups.currentRow()) + current_item = self.mixerGroups.currentItem() + if not current_item: + self.remove_light_btn.setEnabled(False) + return + self.remove_light_btn.setEnabled(True) class RENDERMAN_UL_LightMixer_Group_Members_List(bpy.types.UIList): @@ -843,10 +848,12 @@ def draw_item(self, layout, context, item): classes = [ PRMAN_OT_Renderman_Open_Light_Mixer_Editor, - RENDERMAN_UL_LightMixer_Group_Members_List, - LightMixerQtAppTimed + RENDERMAN_UL_LightMixer_Group_Members_List ] +if not bpy.app.background: + classes.append(LightMixerQtAppTimed) + def register(): from ...rfb_utils import register_utils diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py index 3c70e850..ff8ecaa3 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_tracegroups.py @@ -4,347 +4,349 @@ from ...rman_operators.rman_operators_collections import return_empty_list from ...rman_config import __RFB_CONFIG_DICT__ as rfb_config from ...rfb_utils.prefs_utils import using_qt, show_wip_qt -from ...rman_ui import rfb_qt as rfb_qt import bpy import re import sys -from PySide2 import QtCore, QtWidgets, QtGui __TRACE_GROUPS_WINDOW__ = None -class TraceGroupsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.trace_groups_qt_app_timed" - bl_label = "RenderMan Trace Sets Editor" +if not bpy.app.background: + from ...rman_ui import rfb_qt as rfb_qt + from PySide2 import QtCore, QtWidgets, QtGui - def __init__(self): - super(TraceGroupsQtAppTimed, self).__init__() + class TraceGroupsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.trace_groups_qt_app_timed" + bl_label = "RenderMan Trace Sets Editor" - def execute(self, context): - self._window = TraceGroupsQtWrapper() - return super(TraceGroupsQtAppTimed, self).execute(context) - -class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - -class TraceGroupsQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(TraceGroupsQtWrapper, self).__init__() - - self.setWindowTitle('RenderMan Trace Groups') - self.resize(620, 475) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - - # hide OK and cancel buttons - #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - - self.buttonBox.setObjectName("buttonBox") - self.addButton = QtWidgets.QPushButton(self) - self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) - self.addButton.setObjectName("addButton") - self.addButton.setText("+") - self.removeButton = QtWidgets.QPushButton(self) - self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) - self.removeButton.setObjectName("removeButton") - self.removeButton.setText("-") - - self.traceGroupObjects = QtWidgets.QTreeView(self) - self.traceGroupObjects.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.traceGroupObjects.setModel(self.treeModel) - - self.traceGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) - self.traceGroupObjects.setObjectName("traceGroupObjects") - self.traceGroupObjects.setSelectionMode( - QtWidgets.QAbstractItemView.MultiSelection - ) - - self.traceGroups = QtWidgets.QListWidget(self) - self.traceGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) - self.traceGroups.setObjectName("traceGroups") - - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) - self.label.setText("Trace Groups") - - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) - self.label_2.setText("Objects") - - self.refresh_btn = QtWidgets.QPushButton(self) - self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) - self.refresh_btn.setText("Refresh") - self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) - - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) - QtCore.QMetaObject.connectSlotsByName(self) - - self.addButton.clicked.connect(self.add_group) - self.removeButton.clicked.connect(self.remove_group) - self.refresh_btn.clicked.connect(self.refresh_group_objects) - - self.traceGroups.itemChanged.connect(self.trace_group_changed) - self.traceGroups.itemSelectionChanged.connect(self.trace_groups_index_changed) - self.traceGroupObjects.selectionModel().selectionChanged.connect(self.trace_group_objects_selection) - - self.refresh_groups() - self.refresh_group_objects() - self.checkTraceGroups() - - self.traceGroupObjects.expandAll() - - self.add_handlers() - - def closeEvent(self, event): - self.remove_handlers() - super(TraceGroupsQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Collection): - #self.refresh_groups() - self.traceGroups.setCurrentRow(-1) - self.refresh_group_objects() - #elif isinstance(dps_update.id, bpy.types.Scene): - # self.trace_groups_index_changed() - - def checkTraceGroups(self): - if self.traceGroups.count() < 1: - self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.enable_trace_group_objects(self.rootNode, enable=False) - self.removeButton.setEnabled(False) - else: - self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) - self.removeButton.setEnabled(True) - self.enable_trace_group_objects(self.rootNode, enable=True) - - def update(self): - idx = int(self.traceGroups.currentRow()) - self.addButton.setEnabled(True) + def __init__(self): + super(TraceGroupsQtAppTimed, self).__init__() - self.checkTraceGroups() - super(TraceGroupsQtWrapper, self).update() + def execute(self, context): + self._window = TraceGroupsQtWrapper() + return super(TraceGroupsQtAppTimed, self).execute(context) - def refresh_groups(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - self.traceGroups.clear() - for grp in rm.object_groups: - item = QtWidgets.QListWidgetItem(grp.name) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - self.traceGroups.addItem(item) - - if self.traceGroups.count() > 0: - self.traceGroups.setCurrentRow(rm.object_groups_index) - - def add_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.object_groups.add() - grp.name = 'traceGroup_%d' % len(rm.object_groups) - rm.object_groups_index = len(rm.object_groups)-1 - self.refresh_groups() - - def remove_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) - index = rm.object_groups_index - group = rm.object_groups[index] - # get a list of all objects in this group - ob_list = [member.ob_pointer for member in group.members] - rm.object_groups.remove(index) - rm.object_groups_index -= 1 - - # now tell each object to update - for ob in ob_list: - ob.update_tag(refresh={'OBJECT'}) - - self.refresh_groups() - - def trace_group_changed(self, item): - idx = int(self.traceGroups.currentRow()) - - context = bpy.context - scene = context.scene - rm = scene.renderman - grp = rm.object_groups[idx] - grp.name = item.text() - self.label_2.setText("Objects (%s)" % item.text()) - for member in grp.members: - ob = member.ob_pointer - ob.update_tag(refresh={'OBJECT'}) - - def find_item(self, standard_item, ob): - ''' - if standard_item.text() == ob.name: - return standard_item + class TraceGroupsQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(TraceGroupsQtWrapper, self).__init__() - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - if item.hasChildren(): - return self.find_item(item, ob) - ''' - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - - return None - - def enable_trace_group_objects(self, standard_item, enable=True): - standard_item.setEnabled(enable) - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - item.setEnabled(enable) - if item.hasChildren(): - return self.enable_trace_group_objects(item, enable=enable) - - def refresh_group_objects(self): - idx = int(self.traceGroups.currentRow()) - enabled = True - if idx == -1: - enabled = False - self.label_2.setText("Objects (no group selected)") - context = bpy.context - scene = context.scene - rm = scene.renderman - - self.treeModel.clear() - self.rootNode = self.treeModel.invisibleRootItem() - - def add_children(root_item, ob): - for child in ob.children: - if child.type in ['CAMERA', 'ARMATURE']: - continue - item = self.find_item(root_item, child) - if not item: - item = StandardItem(txt=child.name) - root_item.appendRow(item) - if len(child.children) > 0: - add_children(item, child) - - root_parents = [ob for ob in scene.objects if ob.parent is None] - for ob in root_parents: - if ob.type in ('ARMATURE', 'CAMERA'): - continue + self.setWindowTitle('RenderMan Trace Groups') + self.resize(620, 475) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - item = self.find_item(self.rootNode, ob) - if not item: - item = StandardItem(txt=ob.name) - self.rootNode.appendRow(item) - if len(ob.children) > 0: - add_children(item, ob) - - self.traceGroupObjects.expandAll() - if idx != -1: - self.trace_groups_index_changed() - - def bl_select_objects(self, obs): - context = bpy.context - for ob in context.selected_objects: - ob.select_set(False) - for ob in obs: - ob.select_set(True) - context.view_layer.objects.active = ob - - def trace_groups_index_changed(self): - idx = int(self.traceGroups.currentRow()) - current_item = self.traceGroups.currentItem() - self.checkTraceGroups() - if current_item: - self.label_2.setText("Objects (%s)" % current_item.text()) - else: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.object_groups_index = idx - - group_index = rm.object_groups_index - object_groups = rm.object_groups - object_group = object_groups[group_index] - - selected_items = QtCore.QItemSelection() - obs = [] - for member in object_group.members: - ob = member.ob_pointer - if ob is None: - continue - item = self.find_item(self.rootNode, ob) - if item: - idx = self.treeModel.indexFromItem(item) - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - obs.append(ob) - self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - self.bl_select_objects(obs) + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + + self.traceGroupObjects = QtWidgets.QTreeView(self) + self.traceGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.traceGroupObjects.setModel(self.treeModel) + + self.traceGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) + self.traceGroupObjects.setObjectName("traceGroupObjects") + self.traceGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + + self.traceGroups = QtWidgets.QListWidget(self) + self.traceGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.traceGroups.setObjectName("traceGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) + self.label.setText("Trace Groups") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Objects") + + self.refresh_btn = QtWidgets.QPushButton(self) + self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) + self.refresh_btn.setText("Refresh") + self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + QtCore.QMetaObject.connectSlotsByName(self) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.refresh_btn.clicked.connect(self.refresh_group_objects) + + self.traceGroups.itemChanged.connect(self.trace_group_changed) + self.traceGroups.itemSelectionChanged.connect(self.trace_groups_index_changed) + self.traceGroupObjects.selectionModel().selectionChanged.connect(self.trace_group_objects_selection) + + self.refresh_groups() + self.refresh_group_objects() + self.checkTraceGroups() + + self.traceGroupObjects.expandAll() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(TraceGroupsQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + #self.refresh_groups() + self.traceGroups.setCurrentRow(-1) + self.refresh_group_objects() + #elif isinstance(dps_update.id, bpy.types.Scene): + # self.trace_groups_index_changed() + + def checkTraceGroups(self): + if self.traceGroups.count() < 1: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.enable_trace_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.traceGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.removeButton.setEnabled(True) + self.enable_trace_group_objects(self.rootNode, enable=True) + + def update(self): + idx = int(self.traceGroups.currentRow()) + self.addButton.setEnabled(True) + + self.checkTraceGroups() + super(TraceGroupsQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.traceGroups.clear() + for grp in rm.object_groups: + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.traceGroups.addItem(item) + + if self.traceGroups.count() > 0: + self.traceGroups.setCurrentRow(rm.object_groups_index) - def trace_group_objects_selection(self, selected, deselected): - idx = int(self.traceGroups.currentRow()) - current_item = self.traceGroups.currentItem() - if not current_item: - return - - context = bpy.context - scene = context.scene - rm = scene.renderman - - group_index = rm.object_groups_index - object_groups = rm.object_groups - if group_index not in range(0, len(object_groups)): - return - object_group = object_groups[group_index] - - for i in deselected.indexes(): - item = self.traceGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - for i, member in enumerate(object_group.members): - if ob == member.ob_pointer: - object_group.members.remove(i) - ob.update_tag(refresh={'OBJECT'}) - break - - obs = [] - for i in selected.indexes(): - item = self.traceGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_add = True - for member in object_group.members: - if ob == member.ob_pointer: - do_add = False - obs.append(member.ob_pointer) - if do_add: - obs.append(ob) - ob_in_group = object_group.members.add() - ob_in_group.name = ob.name - ob_in_group.ob_pointer = ob - ob.update_tag(refresh={'OBJECT'}) - self.bl_select_objects(obs) + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.object_groups.add() + grp.name = 'traceGroup_%d' % len(rm.object_groups) + rm.object_groups_index = len(rm.object_groups)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.object_groups_index + group = rm.object_groups[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.object_groups.remove(index) + rm.object_groups_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'OBJECT'}) + + self.refresh_groups() + + def trace_group_changed(self, item): + idx = int(self.traceGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.object_groups[idx] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.ob_pointer + ob.update_tag(refresh={'OBJECT'}) + + def find_item(self, standard_item, ob): + ''' + if standard_item.text() == ob.name: + return standard_item + + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + if item.hasChildren(): + return self.find_item(item, ob) + ''' + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item + + return None + + def enable_trace_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.enable_trace_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.traceGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + context = bpy.context + scene = context.scene + rm = scene.renderman + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + def add_children(root_item, ob): + for child in ob.children: + if child.type in ['CAMERA', 'ARMATURE']: + continue + item = self.find_item(root_item, child) + if not item: + item = StandardItem(txt=child.name) + root_item.appendRow(item) + if len(child.children) > 0: + add_children(item, child) + + root_parents = [ob for ob in scene.objects if ob.parent is None] + for ob in root_parents: + if ob.type in ('ARMATURE', 'CAMERA'): + continue + + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + if len(ob.children) > 0: + add_children(item, ob) + + self.traceGroupObjects.expandAll() + if idx != -1: + self.trace_groups_index_changed() + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + + def trace_groups_index_changed(self): + idx = int(self.traceGroups.currentRow()) + current_item = self.traceGroups.currentItem() + self.checkTraceGroups() + if current_item: + self.label_2.setText("Objects (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.object_groups_index = idx + + group_index = rm.object_groups_index + object_groups = rm.object_groups + object_group = object_groups[group_index] + + selected_items = QtCore.QItemSelection() + obs = [] + for member in object_group.members: + ob = member.ob_pointer + if ob is None: + continue + item = self.find_item(self.rootNode, ob) + if item: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + obs.append(ob) + self.traceGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + self.bl_select_objects(obs) + + def trace_group_objects_selection(self, selected, deselected): + idx = int(self.traceGroups.currentRow()) + current_item = self.traceGroups.currentItem() + if not current_item: + return + + context = bpy.context + scene = context.scene + rm = scene.renderman + + group_index = rm.object_groups_index + object_groups = rm.object_groups + if group_index not in range(0, len(object_groups)): + return + object_group = object_groups[group_index] + + for i in deselected.indexes(): + item = self.traceGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + for i, member in enumerate(object_group.members): + if ob == member.ob_pointer: + object_group.members.remove(i) + ob.update_tag(refresh={'OBJECT'}) + break + + obs = [] + for i in selected.indexes(): + item = self.traceGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in object_group.members: + if ob == member.ob_pointer: + do_add = False + obs.append(member.ob_pointer) + if do_add: + obs.append(ob) + ob_in_group = object_group.members.add() + ob_in_group.name = ob.name + ob_in_group.ob_pointer = ob + ob.update_tag(refresh={'OBJECT'}) + self.bl_select_objects(obs) class RENDERMAN_UL_Object_Group_List(bpy.types.UIList): @@ -517,8 +519,11 @@ def invoke(self, context, event): classes = [ PRMAN_OT_Renderman_Open_Groups_Editor, RENDERMAN_UL_Object_Group_List, - TraceGroupsQtAppTimed ] + +if not bpy.app.background: + classes.append(TraceGroupsQtAppTimed) + def register(): from ...rfb_utils import register_utils diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py index e532794a..f5d895fb 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_vol_aggregates.py @@ -6,320 +6,322 @@ from ...rfb_utils import scene_utils from ... import rfb_icons from ...rfb_utils.prefs_utils import using_qt, show_wip_qt -from ...rman_ui import rfb_qt as rfb_qt import bpy import re import sys -from PySide2 import QtCore, QtWidgets, QtGui __VOL_AGGREGATE_WINDOW__ = None -class VolAggregateQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.vol_aggregates_qt_app_timed" - bl_label = "RenderMan Volume Aggregates Editor" +if not bpy.app.background: + from ...rman_ui import rfb_qt as rfb_qt + from PySide2 import QtCore, QtWidgets, QtGui - def __init__(self): - super(VolAggregateQtAppTimed, self).__init__() + class VolAggregateQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.vol_aggregates_qt_app_timed" + bl_label = "RenderMan Volume Aggregates Editor" - def execute(self, context): - self._window = VolAggregatesQtWrapper() - return super(VolAggregateQtAppTimed, self).execute(context) - -class StandardItem(QtGui.QStandardItem): - def __init__(self, txt=''): - super().__init__() - self.setEditable(False) - self.setText(txt) - -class VolAggregatesQtWrapper(rfb_qt.RmanQtWrapper): - def __init__(self) -> None: - super(VolAggregatesQtWrapper, self).__init__() - - self.setWindowTitle('RenderMan Volume Aggregates') - self.resize(620, 475) - self.buttonBox = QtWidgets.QDialogButtonBox(self) - self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - - # hide OK and cancel buttons - #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - - self.buttonBox.setObjectName("buttonBox") - self.addButton = QtWidgets.QPushButton(self) - self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) - self.addButton.setObjectName("addButton") - self.addButton.setText("+") - self.removeButton = QtWidgets.QPushButton(self) - self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) - self.removeButton.setObjectName("removeButton") - self.removeButton.setText("-") - - self.volAggregateGroupObjects = QtWidgets.QTreeView(self) - self.volAggregateGroupObjects.setHeaderHidden(True) - self.treeModel = QtGui.QStandardItemModel(self) - self.rootNode = self.treeModel.invisibleRootItem() - self.volAggregateGroupObjects.setModel(self.treeModel) - - self.volAggregateGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) - self.volAggregateGroupObjects.setObjectName("volAggregateGroupObjects") - self.volAggregateGroupObjects.setSelectionMode( - QtWidgets.QAbstractItemView.MultiSelection - ) - - self.volAggregateGroups = QtWidgets.QListWidget(self) - self.volAggregateGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) - self.volAggregateGroups.setObjectName("volAggregateGroups") - - self.label = QtWidgets.QLabel(self) - self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) - self.label.setText("Volume Aggregates") - - self.label_2 = QtWidgets.QLabel(self) - self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) - self.label_2.setText("Objects") - - self.refresh_btn = QtWidgets.QPushButton(self) - self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) - self.refresh_btn.setText("Refresh") - self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) - - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) - QtCore.QMetaObject.connectSlotsByName(self) - - self.addButton.clicked.connect(self.add_group) - self.removeButton.clicked.connect(self.remove_group) - self.refresh_btn.clicked.connect(self.refresh_group_objects) - - self.volAggregateGroups.itemChanged.connect(self.vol_aggregate_group_changed) - self.volAggregateGroups.itemSelectionChanged.connect(self.vol_aggregate_groups_index_changed) - self.volAggregateGroupObjects.selectionModel().selectionChanged.connect(self.vol_aggregate_group_objects_selection) - - self.refresh_groups() - self.refresh_group_objects() - self.checkvolAggregateGroups() - - self.volAggregateGroupObjects.expandAll() - - self.add_handlers() - - def closeEvent(self, event): - self.remove_handlers() - super(VolAggregatesQtWrapper, self).closeEvent(event) - - def add_handlers(self): - if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) - - def remove_handlers(self): - if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: - bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) - - def depsgraph_update_post(self, bl_scene, depsgraph): - for dps_update in reversed(depsgraph.updates): - if isinstance(dps_update.id, bpy.types.Collection): - self.volAggregateGroups.setCurrentRow(-1) - self.refresh_group_objects() - - def checkvolAggregateGroups(self): - if self.volAggregateGroups.count() < 1: - self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) - self.vol_aggregate_group_objects(self.rootNode, enable=False) - self.removeButton.setEnabled(False) - else: - self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) - self.removeButton.setEnabled(True) - self.vol_aggregate_group_objects(self.rootNode, enable=True) + def __init__(self): + super(VolAggregateQtAppTimed, self).__init__() - def update(self): - idx = int(self.volAggregateGroups.currentRow()) - self.addButton.setEnabled(True) + def execute(self, context): + self._window = VolAggregatesQtWrapper() + return super(VolAggregateQtAppTimed, self).execute(context) - self.checkvolAggregateGroups() - super(VolAggregatesQtWrapper, self).update() + class StandardItem(QtGui.QStandardItem): + def __init__(self, txt=''): + super().__init__() + self.setEditable(False) + self.setText(txt) - def refresh_groups(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - self.volAggregateGroups.clear() - for i, grp in enumerate(rm.vol_aggregates): - if i == 0: - continue - item = QtWidgets.QListWidgetItem(grp.name) - item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) - self.volAggregateGroups.addItem(item) - - if self.volAggregateGroups.count() > 0: - self.volAggregateGroups.setCurrentRow(rm.vol_aggregates_index) + class VolAggregatesQtWrapper(rfb_qt.RmanQtWrapper): + def __init__(self) -> None: + super(VolAggregatesQtWrapper, self).__init__() + + self.setWindowTitle('RenderMan Volume Aggregates') + self.resize(620, 475) + self.buttonBox = QtWidgets.QDialogButtonBox(self) + self.buttonBox.setGeometry(QtCore.QRect(260, 440, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - def add_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - grp = rm.vol_aggregates.add() - grp.name = 'VolumeAggreagte_%d' % (len(rm.vol_aggregates)-2) - rm.vol_aggregates_index = len(rm.vol_aggregates)-1 - self.refresh_groups() - - def remove_group(self): - context = bpy.context - scene = context.scene - rm = scene.renderman - - index = rm.vol_aggregates_index - group = rm.vol_aggregates[index] - # get a list of all objects in this group - ob_list = [member.ob_pointer for member in group.members] - rm.vol_aggregates.remove(index) - rm.vol_aggregates_index -= 1 - - # now tell each object to update - for ob in ob_list: - ob.update_tag(refresh={'DATA'}) - - self.refresh_groups() - - def vol_aggregate_group_changed(self, item): - idx = int(self.volAggregateGroups.currentRow()) + # hide OK and cancel buttons + #self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + + self.buttonBox.setObjectName("buttonBox") + self.addButton = QtWidgets.QPushButton(self) + self.addButton.setGeometry(QtCore.QRect(280, 30, 31, 26)) + self.addButton.setObjectName("addButton") + self.addButton.setText("+") + self.removeButton = QtWidgets.QPushButton(self) + self.removeButton.setGeometry(QtCore.QRect(280, 50, 31, 26)) + self.removeButton.setObjectName("removeButton") + self.removeButton.setText("-") + + self.volAggregateGroupObjects = QtWidgets.QTreeView(self) + self.volAggregateGroupObjects.setHeaderHidden(True) + self.treeModel = QtGui.QStandardItemModel(self) + self.rootNode = self.treeModel.invisibleRootItem() + self.volAggregateGroupObjects.setModel(self.treeModel) + + self.volAggregateGroupObjects.setGeometry(QtCore.QRect(30, 250, 441, 192)) + self.volAggregateGroupObjects.setObjectName("volAggregateGroupObjects") + self.volAggregateGroupObjects.setSelectionMode( + QtWidgets.QAbstractItemView.MultiSelection + ) + + self.volAggregateGroups = QtWidgets.QListWidget(self) + self.volAggregateGroups.setGeometry(QtCore.QRect(30, 30, 256, 192)) + self.volAggregateGroups.setObjectName("volAggregateGroups") + + self.label = QtWidgets.QLabel(self) + self.label.setGeometry(QtCore.QRect(40, 10, 91, 17)) + self.label.setText("Volume Aggregates") + + self.label_2 = QtWidgets.QLabel(self) + self.label_2.setGeometry(QtCore.QRect(40, 230, 200, 17)) + self.label_2.setText("Objects") + + self.refresh_btn = QtWidgets.QPushButton(self) + self.refresh_btn.setGeometry(QtCore.QRect(470, 250, 100, 26)) + self.refresh_btn.setText("Refresh") + self.setToolTip("""Click this if the objects list is out of sync with the scene""" ) + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + QtCore.QMetaObject.connectSlotsByName(self) + + self.addButton.clicked.connect(self.add_group) + self.removeButton.clicked.connect(self.remove_group) + self.refresh_btn.clicked.connect(self.refresh_group_objects) + + self.volAggregateGroups.itemChanged.connect(self.vol_aggregate_group_changed) + self.volAggregateGroups.itemSelectionChanged.connect(self.vol_aggregate_groups_index_changed) + self.volAggregateGroupObjects.selectionModel().selectionChanged.connect(self.vol_aggregate_group_objects_selection) + + self.refresh_groups() + self.refresh_group_objects() + self.checkvolAggregateGroups() + + self.volAggregateGroupObjects.expandAll() + + self.add_handlers() + + def closeEvent(self, event): + self.remove_handlers() + super(VolAggregatesQtWrapper, self).closeEvent(event) + + def add_handlers(self): + if self.depsgraph_update_post not in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.append(self.depsgraph_update_post) + + def remove_handlers(self): + if self.depsgraph_update_post in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(self.depsgraph_update_post) + + def depsgraph_update_post(self, bl_scene, depsgraph): + for dps_update in reversed(depsgraph.updates): + if isinstance(dps_update.id, bpy.types.Collection): + self.volAggregateGroups.setCurrentRow(-1) + self.refresh_group_objects() + + def checkvolAggregateGroups(self): + if self.volAggregateGroups.count() < 1: + self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.vol_aggregate_group_objects(self.rootNode, enable=False) + self.removeButton.setEnabled(False) + else: + self.volAggregateGroupObjects.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.removeButton.setEnabled(True) + self.vol_aggregate_group_objects(self.rootNode, enable=True) + + def update(self): + idx = int(self.volAggregateGroups.currentRow()) + self.addButton.setEnabled(True) + + self.checkvolAggregateGroups() + super(VolAggregatesQtWrapper, self).update() + + def refresh_groups(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + self.volAggregateGroups.clear() + for i, grp in enumerate(rm.vol_aggregates): + if i == 0: + continue + item = QtWidgets.QListWidgetItem(grp.name) + item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) + self.volAggregateGroups.addItem(item) - context = bpy.context - scene = context.scene - rm = scene.renderman - grp = rm.vol_aggregates[idx+1] - grp.name = item.text() - self.label_2.setText("Objects (%s)" % item.text()) - for member in grp.members: - ob = member.ob_pointer - ob.update_tag(refresh={'DATA'}) - - def find_item(self, standard_item, ob): - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - if item.text() == ob.name: - return item - - return None - - def vol_aggregate_group_objects(self, standard_item, enable=True): - standard_item.setEnabled(enable) - for i in range(0, standard_item.rowCount()): - item = standard_item.child(i) - item.setEnabled(enable) - if item.hasChildren(): - return self.vol_aggregate_group_objects(item, enable=enable) - - def refresh_group_objects(self): - idx = int(self.volAggregateGroups.currentRow()) - enabled = True - if idx == -1: - enabled = False - self.label_2.setText("Objects (no group selected)") - context = bpy.context - scene = context.scene - rm = scene.renderman - - self.treeModel.clear() - self.rootNode = self.treeModel.invisibleRootItem() - - root_parents = scene_utils.get_all_volume_objects(scene) - for ob in root_parents: + if self.volAggregateGroups.count() > 0: + self.volAggregateGroups.setCurrentRow(rm.vol_aggregates_index) + + def add_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + grp = rm.vol_aggregates.add() + grp.name = 'VolumeAggreagte_%d' % (len(rm.vol_aggregates)-2) + rm.vol_aggregates_index = len(rm.vol_aggregates)-1 + self.refresh_groups() + + def remove_group(self): + context = bpy.context + scene = context.scene + rm = scene.renderman + + index = rm.vol_aggregates_index + group = rm.vol_aggregates[index] + # get a list of all objects in this group + ob_list = [member.ob_pointer for member in group.members] + rm.vol_aggregates.remove(index) + rm.vol_aggregates_index -= 1 + + # now tell each object to update + for ob in ob_list: + ob.update_tag(refresh={'DATA'}) + + self.refresh_groups() + + def vol_aggregate_group_changed(self, item): + idx = int(self.volAggregateGroups.currentRow()) + + context = bpy.context + scene = context.scene + rm = scene.renderman + grp = rm.vol_aggregates[idx+1] + grp.name = item.text() + self.label_2.setText("Objects (%s)" % item.text()) + for member in grp.members: + ob = member.ob_pointer + ob.update_tag(refresh={'DATA'}) + + def find_item(self, standard_item, ob): + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + if item.text() == ob.name: + return item - item = self.find_item(self.rootNode, ob) - if not item: - item = StandardItem(txt=ob.name) - self.rootNode.appendRow(item) - - self.volAggregateGroupObjects.expandAll() - if idx != -1: - self.vol_aggregate_groups_index_changed() - - def bl_select_objects(self, obs): - context = bpy.context - for ob in context.selected_objects: - ob.select_set(False) - for ob in obs: - ob.select_set(True) - context.view_layer.objects.active = ob - - def vol_aggregate_groups_index_changed(self): - idx = int(self.volAggregateGroups.currentRow()) - current_item = self.volAggregateGroups.currentItem() - self.checkvolAggregateGroups() - if current_item: - self.label_2.setText("Objects (%s)" % current_item.text()) - else: - return - context = bpy.context - scene = context.scene - rm = scene.renderman - rm.vol_aggregates_index = idx + 1 - - group_index = rm.vol_aggregates_index - vol_aggregates = rm.vol_aggregates - object_group = vol_aggregates[group_index] - - selected_items = QtCore.QItemSelection() - obs = [] - for member in object_group.members: - ob = member.ob_pointer - if ob is None: - continue - item = self.find_item(self.rootNode, ob) - if item: - idx = self.treeModel.indexFromItem(item) - selection_range = QtCore.QItemSelectionRange(idx) - selected_items.append(selection_range) - obs.append(ob) - self.volAggregateGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) - self.bl_select_objects(obs) + return None + + def vol_aggregate_group_objects(self, standard_item, enable=True): + standard_item.setEnabled(enable) + for i in range(0, standard_item.rowCount()): + item = standard_item.child(i) + item.setEnabled(enable) + if item.hasChildren(): + return self.vol_aggregate_group_objects(item, enable=enable) + + def refresh_group_objects(self): + idx = int(self.volAggregateGroups.currentRow()) + enabled = True + if idx == -1: + enabled = False + self.label_2.setText("Objects (no group selected)") + context = bpy.context + scene = context.scene + rm = scene.renderman + + self.treeModel.clear() + self.rootNode = self.treeModel.invisibleRootItem() + + root_parents = scene_utils.get_all_volume_objects(scene) + for ob in root_parents: - def vol_aggregate_group_objects_selection(self, selected, deselected): - idx = int(self.volAggregateGroups.currentRow()) - current_item = self.volAggregateGroups.currentItem() - if not current_item: - return - - context = bpy.context - scene = context.scene - rm = scene.renderman - - group_index = rm.vol_aggregates_index - vol_aggregates = rm.vol_aggregates - if group_index not in range(0, len(vol_aggregates)): - return - object_group = vol_aggregates[group_index] - - for i in deselected.indexes(): - item = self.volAggregateGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - for i, member in enumerate(object_group.members): - if ob == member.ob_pointer: - object_group.members.remove(i) - ob.update_tag(refresh={'DATA'}) - break - - obs = [] - for i in selected.indexes(): - item = self.volAggregateGroupObjects.model().itemFromIndex(i) - ob = bpy.data.objects.get(item.text(), None) - if ob is None: - continue - do_add = True - for member in object_group.members: - if ob == member.ob_pointer: - do_add = False - obs.append(member.ob_pointer) - if do_add: - obs.append(ob) - ob_in_group = object_group.members.add() - ob_in_group.name = ob.name - ob_in_group.ob_pointer = ob - ob.update_tag(refresh={'DATA'}) - self.bl_select_objects(obs) + item = self.find_item(self.rootNode, ob) + if not item: + item = StandardItem(txt=ob.name) + self.rootNode.appendRow(item) + + self.volAggregateGroupObjects.expandAll() + if idx != -1: + self.vol_aggregate_groups_index_changed() + + def bl_select_objects(self, obs): + context = bpy.context + for ob in context.selected_objects: + ob.select_set(False) + for ob in obs: + ob.select_set(True) + context.view_layer.objects.active = ob + + def vol_aggregate_groups_index_changed(self): + idx = int(self.volAggregateGroups.currentRow()) + current_item = self.volAggregateGroups.currentItem() + self.checkvolAggregateGroups() + if current_item: + self.label_2.setText("Objects (%s)" % current_item.text()) + else: + return + context = bpy.context + scene = context.scene + rm = scene.renderman + rm.vol_aggregates_index = idx + 1 + + group_index = rm.vol_aggregates_index + vol_aggregates = rm.vol_aggregates + object_group = vol_aggregates[group_index] + + selected_items = QtCore.QItemSelection() + obs = [] + for member in object_group.members: + ob = member.ob_pointer + if ob is None: + continue + item = self.find_item(self.rootNode, ob) + if item: + idx = self.treeModel.indexFromItem(item) + selection_range = QtCore.QItemSelectionRange(idx) + selected_items.append(selection_range) + obs.append(ob) + self.volAggregateGroupObjects.selectionModel().select(selected_items, QtCore.QItemSelectionModel.ClearAndSelect | QtCore.QItemSelectionModel.NoUpdate) + self.bl_select_objects(obs) + + def vol_aggregate_group_objects_selection(self, selected, deselected): + idx = int(self.volAggregateGroups.currentRow()) + current_item = self.volAggregateGroups.currentItem() + if not current_item: + return + + context = bpy.context + scene = context.scene + rm = scene.renderman + + group_index = rm.vol_aggregates_index + vol_aggregates = rm.vol_aggregates + if group_index not in range(0, len(vol_aggregates)): + return + object_group = vol_aggregates[group_index] + + for i in deselected.indexes(): + item = self.volAggregateGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + for i, member in enumerate(object_group.members): + if ob == member.ob_pointer: + object_group.members.remove(i) + ob.update_tag(refresh={'DATA'}) + break + + obs = [] + for i in selected.indexes(): + item = self.volAggregateGroupObjects.model().itemFromIndex(i) + ob = bpy.data.objects.get(item.text(), None) + if ob is None: + continue + do_add = True + for member in object_group.members: + if ob == member.ob_pointer: + do_add = False + obs.append(member.ob_pointer) + if do_add: + obs.append(ob) + ob_in_group = object_group.members.add() + ob_in_group.name = ob.name + ob_in_group.ob_pointer = ob + ob.update_tag(refresh={'DATA'}) + self.bl_select_objects(obs) class RENDERMAN_UL_Volume_Aggregates_List(bpy.types.UIList): @@ -540,9 +542,11 @@ def invoke(self, context, event): PRMAN_OT_Renderman_Open_Volume_Aggregates_Editor, RENDERMAN_UL_Volume_Aggregates_List, RENDERMAN_UL_Volume_Aggregates_Objects_List, - VolAggregateQtAppTimed ] +if not bpy.app.background: + classes.append(VolAggregateQtAppTimed) + def register(): from ...rfb_utils import register_utils diff --git a/rman_presets/__init__.py b/rman_presets/__init__.py index d32e1dba..aae093c1 100644 --- a/rman_presets/__init__.py +++ b/rman_presets/__init__.py @@ -23,30 +23,20 @@ # # ##### END MIT LICENSE BLOCK ##### +import bpy from . import properties -from . import ui +if not bpy.app.background: + from . import ui from . import operators -from ..rfb_utils.prefs_utils import get_pref -import os def register(): properties.register() - if get_pref('rman_ui_framework') == 'QT': - try: - from . import qt_app - qt_app.register() - except: - pass - ui.register() + if not bpy.app.background: + ui.register() operators.register() def unregister(): properties.unregister() - if get_pref('rman_ui_framework') == 'QT': - try: - from . import qt_app - qt_app.unregister() - except: - pass - ui.unregister() + if not bpy.app.background: + ui.unregister() operators.unregister() \ No newline at end of file diff --git a/rman_spool.py b/rman_spool.py index c20391fd..17a1bc1b 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -48,8 +48,6 @@ def add_job_level_attrs(self, job): envkeys = [] if self.is_localqueue: envkeys.append('rmantree=%s' % envconfig().rmantree) - else: - envkeys.append('prman-%s' % rman_vers) user_envkeys = self.tractor_cfg.get('envkeys', get_pref('rman_tractor_envkeys')) job.envkey = envkeys + user_envkeys.split() diff --git a/rman_stats/operators.py b/rman_stats/operators.py index a5e8e341..c443bc0a 100644 --- a/rman_stats/operators.py +++ b/rman_stats/operators.py @@ -1,4 +1,3 @@ -from ..rman_ui import rfb_qt import bpy import sys from ..rfb_logger import rfb_log @@ -6,74 +5,78 @@ __STATS_WINDOW__ = None -class LiveStatsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.live_stats_qt_app_timed" - bl_label = "Live Stats" +if not bpy.app.background: + from ..rman_ui import rfb_qt - def __init__(self): - super(LiveStatsQtAppTimed, self).__init__() + class LiveStatsQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.live_stats_qt_app_timed" + bl_label = "Live Stats" - def execute(self, context): - global __STATS_WINDOW__ - __STATS_WINDOW__ = RmanStatsWrapper() - self._window = __STATS_WINDOW__ - return super(LiveStatsQtAppTimed, self).execute(context) - -class RmanStatsWrapper(rfb_qt.RmanQtWrapper): - - def __init__(self): - super(RmanStatsWrapper, self).__init__() - - # import here because we will crash Blender - # when we try to import it globally - import rman_utils.stats_config.ui as rui + def __init__(self): + super(LiveStatsQtAppTimed, self).__init__() - self.resize(512, 512) - self.setWindowTitle('RenderMan Live Stats') + def execute(self, context): + global __STATS_WINDOW__ + __STATS_WINDOW__ = RmanStatsWrapper() + self._window = __STATS_WINDOW__ + return super(LiveStatsQtAppTimed, self).execute(context) - rr = rman_render.RmanRender.get_rman_render() - mgr = rr.stats_mgr.mgr - self.ui = rui.StatsManagerUI(self, manager=mgr, show_connect=True, show_config=False) - self.setLayout(self.ui.topLayout) - self.show() # Show window - - def show(self): - if not self.ui.manager.clientConnected(): - self.ui.attachCB() - else: - # This is a bit weird. If the stats manager is already - # connected, the UI doesn't seem to update the connection status when - # first showing the window. - # For now, just kick the UI's connectedTimer - self.ui.connectedTimer.start(1000) - self.ui.attachBtn.setText("Connecting...") - - super(RmanStatsWrapper, self).show() - - def closeEvent(self, event): - event.accept() - -class PRMAN_OT_Open_Stats(bpy.types.Operator): - bl_idname = "renderman.rman_open_stats" - bl_label = "Live Stats" - - def execute(self, context): - - global __STATS_WINDOW__ - if __STATS_WINDOW__ and __STATS_WINDOW__.isVisible(): + class RmanStatsWrapper(rfb_qt.RmanQtWrapper): + + def __init__(self): + super(RmanStatsWrapper, self).__init__() + + # import here because we will crash Blender + # when we try to import it globally + import rman_utils.stats_config.ui as rui + + self.resize(512, 512) + self.setWindowTitle('RenderMan Live Stats') + + rr = rman_render.RmanRender.get_rman_render() + mgr = rr.stats_mgr.mgr + self.ui = rui.StatsManagerUI(self, manager=mgr, show_connect=True, show_config=False) + self.setLayout(self.ui.topLayout) + self.show() # Show window + + def show(self): + if not self.ui.manager.clientConnected(): + self.ui.attachCB() + else: + # This is a bit weird. If the stats manager is already + # connected, the UI doesn't seem to update the connection status when + # first showing the window. + # For now, just kick the UI's connectedTimer + self.ui.connectedTimer.start(1000) + self.ui.attachBtn.setText("Connecting...") + + super(RmanStatsWrapper, self).show() + + def closeEvent(self, event): + event.accept() + + class PRMAN_OT_Open_Stats(bpy.types.Operator): + bl_idname = "renderman.rman_open_stats" + bl_label = "Live Stats" + + def execute(self, context): + + global __STATS_WINDOW__ + if __STATS_WINDOW__ and __STATS_WINDOW__.isVisible(): + return {'FINISHED'} + + if sys.platform == "darwin": + __STATS_WINDOW__ = rfb_qt.run_with_timer(__STATS_WINDOW__, RmanStatsWrapper) + else: + bpy.ops.wm.live_stats_qt_app_timed() + return {'FINISHED'} - if sys.platform == "darwin": - __STATS_WINDOW__ = rfb_qt.run_with_timer(__STATS_WINDOW__, RmanStatsWrapper) - else: - bpy.ops.wm.live_stats_qt_app_timed() - - return {'FINISHED'} - -classes = [ - PRMAN_OT_Open_Stats, - LiveStatsQtAppTimed -] +classes = [] + +if not bpy.app.background: + classes.append(PRMAN_OT_Open_Stats) + classes.append(LiveStatsQtAppTimed) def register(): from ..rfb_utils import register_utils diff --git a/rman_ui/rman_ui_txmanager.py b/rman_ui/rman_ui_txmanager.py index 70a43e0c..19382bb3 100644 --- a/rman_ui/rman_ui_txmanager.py +++ b/rman_ui/rman_ui_txmanager.py @@ -15,63 +15,65 @@ from rman_utils.txmanager import txparams from rman_utils import txmanager as txmngr from .. import rfb_icons -from ..rman_ui import rfb_qt import sys import hashlib import os import uuid -__QT_LOADED__ = True __TXMANAGER_WINDOW__ = None -class TxManagerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): - bl_idname = "wm.txm_qt_app_timed" - bl_label = "Texture Manager" +if not bpy.app.background: + from ..rman_ui import rfb_qt - def __init__(self): - super(TxManagerQtAppTimed, self).__init__() + class TxManagerQtAppTimed(rfb_qt.RfbBaseQtAppTimed): + bl_idname = "wm.txm_qt_app_timed" + bl_label = "Texture Manager" - def execute(self, context): - self._window = create_widget() - return super(TxManagerQtAppTimed, self).execute(context) - -def parse_scene(): - from ..rfb_utils import texture_utils - bl_scene = bpy.context.scene - mgr = texture_utils.get_txmanager().txmanager - mgr.reset() - texture_utils.parse_for_textures(bl_scene) - -def _append_to_tx_list(file_path_list): - """Called by the txmanager when extra files are added to the scene list. - """ - from ..rfb_utils import texture_utils - bl_scene = bpy.context.scene - txmgr = texture_utils.get_txmanager().txmanager - texture_utils.parse_for_textures(bl_scene) - for fpath in file_path_list: - # Pass None as the nodeID and a hash will be generated. - texid = hashlib.sha1(fpath.encode('utf-8')).hexdigest() - txmgr.add_texture(texid, fpath) - txmgr.update_ui_list() - # make sure to restart the queue. - txmgr.txmake_all(start_queue=True, blocking=False) - -def help_func(url): - bpy.ops.wm.url_open(url = RFB_HELP_URL) - -def create_widget(): - global __TXMANAGER_WINDOW__ - if not __TXMANAGER_WINDOW__: - import rman_utils.txmanager.ui as rui - from ..rfb_utils import texture_utils + def __init__(self): + super(TxManagerQtAppTimed, self).__init__() + + def execute(self, context): + self._window = create_widget() + return super(TxManagerQtAppTimed, self).execute(context) + + def parse_scene(): + from ..rfb_utils import texture_utils + bl_scene = bpy.context.scene mgr = texture_utils.get_txmanager().txmanager - __TXMANAGER_WINDOW__ = rui.TxManagerUI(None, txmanager=mgr, - parse_scene_func=parse_scene, - append_tx_func=_append_to_tx_list, - help_func=help_func) - mgr.ui = __TXMANAGER_WINDOW__ - return __TXMANAGER_WINDOW__ + mgr.reset() + texture_utils.parse_for_textures(bl_scene) + + def _append_to_tx_list(file_path_list): + """Called by the txmanager when extra files are added to the scene list. + """ + from ..rfb_utils import texture_utils + bl_scene = bpy.context.scene + txmgr = texture_utils.get_txmanager().txmanager + texture_utils.parse_for_textures(bl_scene) + for fpath in file_path_list: + # Pass None as the nodeID and a hash will be generated. + texid = hashlib.sha1(fpath.encode('utf-8')).hexdigest() + txmgr.add_texture(texid, fpath) + txmgr.update_ui_list() + # make sure to restart the queue. + txmgr.txmake_all(start_queue=True, blocking=False) + + def help_func(url): + bpy.ops.wm.url_open(url = RFB_HELP_URL) + + def create_widget(): + global __TXMANAGER_WINDOW__ + if not __TXMANAGER_WINDOW__: + import rman_utils.txmanager.ui as rui + from ..rfb_utils import texture_utils + mgr = texture_utils.get_txmanager().txmanager + __TXMANAGER_WINDOW__ = rui.TxManagerUI(None, txmanager=mgr, + parse_scene_func=parse_scene, + append_tx_func=_append_to_tx_list, + help_func=help_func) + mgr.ui = __TXMANAGER_WINDOW__ + return __TXMANAGER_WINDOW__ + class TxFileItem(PropertyGroup): """UIList item representing a TxFile""" @@ -837,8 +839,10 @@ def index_updated(self, context): PRMAN_PT_Renderman_txmanager_list, PRMAN_OT_Renderman_txmanager_remove_texture, PRMAN_OT_Renderman_open_txmanager, - TxManagerQtAppTimed -] +] + +if not bpy.app.background: + classes.append(TxManagerQtAppTimed) def register(): From 04324b8e57cf684ec60339c9324cbfca22178154 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 5 Apr 2023 10:42:32 -0700 Subject: [PATCH 269/278] Put back the prman- envkey for tractor jobs. --- rman_spool.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rman_spool.py b/rman_spool.py index 17a1bc1b..c20391fd 100644 --- a/rman_spool.py +++ b/rman_spool.py @@ -48,6 +48,8 @@ def add_job_level_attrs(self, job): envkeys = [] if self.is_localqueue: envkeys.append('rmantree=%s' % envconfig().rmantree) + else: + envkeys.append('prman-%s' % rman_vers) user_envkeys = self.tractor_cfg.get('envkeys', get_pref('rman_tractor_envkeys')) job.envkey = envkeys + user_envkeys.split() From caa8ec84620e210858eda8cc5dd38f57e6223f04 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 5 Apr 2023 10:50:37 -0700 Subject: [PATCH 270/278] Qt light mixer fix. context might not always have a selected_objects. If it's not there, switch to view_layer.objects.selected. --- .../rman_operators_editors_lightmixer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py index 38dcc639..d505ed48 100644 --- a/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py +++ b/rman_operators/rman_operators_editors/rman_operators_editors_lightmixer.py @@ -372,8 +372,11 @@ def depsgraph_update_post(self, bl_scene, depsgraph): def enableAddLightButton(self): context = bpy.context scene = context.scene - rm = scene.renderman - lights = [ob for ob in context.selected_objects if ob.type == "LIGHT"] + rm = scene.renderman + obs = getattr(context, 'selected_objects', None) + if obs is None: + obs = context.view_layer.objects.selected + lights = [ob for ob in obs if ob.type == "LIGHT"] if len(lights) > 0: any_lights = [] if rm.light_mixer_groups_index > 0: @@ -399,7 +402,10 @@ def add_light(self): rm = scene.renderman grp = rm.light_mixer_groups[rm.light_mixer_groups_index] - for ob in context.selected_objects: + obs = getattr(context, 'selected_objects', None) + if obs is None: + obs = context.view_layer.objects.selected + for ob in obs: do_add = True for member in grp.members: if member.light_ob == ob: From 7e3aad5a95a5a35c98bb57dd1618c59599459562 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 6 Apr 2023 12:10:11 -0700 Subject: [PATCH 271/278] Remove the warning beta 2 tag from bl_info --- __init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2429f9ef..ce0cd99c 100644 --- a/__init__.py +++ b/__init__.py @@ -35,7 +35,6 @@ "location": "Render Properties > Render Engine > RenderMan", "description": "RenderMan 25 integration", "doc_url": "https://rmanwiki.pixar.com/display/RFB", - "warning": "BETA 2", "category": "Render"} __RMAN_ADDON_LOADED__ = False From 0dbec116ab27bcfcf36f012b4180c4f83bca7fef Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Tue, 11 Apr 2023 13:07:44 -0700 Subject: [PATCH 272/278] Fix issue where an instanced object would disappear, if the geometry node network was updated. --- rman_scene_sync.py | 62 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index ec6db33e..78eeb021 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -84,6 +84,18 @@ def update_view(self, context, depsgraph): else: translator.update_transform(camera, rman_sg_camera) + + def create_rman_update(self, ob_key, **kwargs): + rman_update = RmanUpdate() + rman_update.is_updated_shading = kwargs.get('update_shading', False) + rman_update.is_updated_transform = kwargs.get('update_transform', False) + rman_update.is_updated_geometry = kwargs.get('update_geometry', False) + rman_update.is_updated_attributes = kwargs.get('update_attributes', False) + rman_update.updated_prop_name = kwargs.get('prop_name', None) + rman_update.do_clear_instances = kwargs.get('clear_instances', True) + self.rman_updates[ob_key] = rman_update + return rman_update + @time_this def scene_updated(self): # Check visible objects @@ -563,6 +575,9 @@ def batch_update_scene(self, context, depsgraph): elif isinstance(dps_update.id, bpy.types.Object): self.check_object_datablock(dps_update) + elif isinstance(dps_update.id, bpy.types.GeometryNodeTree): + # create an empty RmanUpdate + self.create_rman_update(dps_update.id.original, clear_instances=False) if not self.rman_updates and self.num_instances_changed: # The number of instances changed, but we are not able @@ -685,8 +700,10 @@ def update_scene(self, context, depsgraph): elif isinstance(dps_update.id, bpy.types.Collection): rfb_log().debug("Collection updated: %s" % dps_update.id.name) - #self.update_collection(dps_update.id) - + #self.update_collection(dps_update.id) + elif isinstance(dps_update.id, bpy.types.GeometryNodeTree): + # create an empty RmanUpdate + self.create_rman_update(dps_update.id.original, clear_instances=False) else: rfb_log().debug("Not handling %s update: %s" % (str(type(dps_update.id)), dps_update.id.name)) @@ -766,11 +783,9 @@ def check_instances(self, batch_mode=False): is_new_object = True if rman_update is None: - rman_update = RmanUpdate() - rman_update.is_updated_geometry = True - rman_update.is_updated_shading = True - rman_update.is_updated_transform = True - self.rman_updates[ob_key] = rman_update + rman_update = self.create_rman_update(ob_key, update_geometry=False, update_shading=True, update_transform=True) + # set update_geometry to False + # since we've already exported the datablock rman_update.is_updated_geometry = False clear_instances.append(rman_sg_node) @@ -784,15 +799,30 @@ def check_instances(self, batch_mode=False): if rman_sg_node.is_frame_sensitive and self.frame_number_changed: rman_update.is_updated_geometry = True self.rman_updates[ob_key] = rman_update - elif rman_update is None: - # check if the instance_parent was the thing that - # changed - if not instance_parent: - continue - rman_update = self.rman_updates.get(instance_parent.original, None) - if rman_update is None: - continue - rfb_log().debug("\t%s parent updated (%s)" % (ob_eval.name, instance_parent.name)) + elif rman_update is None: + # no RmanUpdate exists for this object + # + # check if one of the users of this object updated + # ex: the object was instanced via a GeometryNodeTree, and the + # geometry node tree updated + users = self.rman_scene.context.blend_data.user_map(subset={ob_eval.original}) + user_exist = False + for o in users[ob_eval.original]: + if o.original in self.rman_updates: + rfb_log().debug("\t%s user updated (%s)" % (ob_eval.name, o.name)) + user_exist = True + break + if user_exist: + rman_update = self.create_rman_update(ob_key, update_transform=True) + else: + # check if the instance_parent was the thing that + # changed + if not instance_parent: + continue + rman_update = self.rman_updates.get(instance_parent.original, None) + if rman_update is None: + continue + rfb_log().debug("\t%s parent updated (%s)" % (ob_eval.name, instance_parent.name)) if rman_sg_node and not is_new_object and not instance.is_instance: if rman_update.is_updated_geometry and proto_key not in already_udpated: From 4e27c87659fc8bf4d139f21658f98cb6241872ef Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Wed, 12 Apr 2023 15:53:38 -0700 Subject: [PATCH 273/278] Modify mesh translator and rman_sg_mesh to add the mesh to a group node. This allows us to remove the mesh via RemoveChild if we get an invalid mesh. --- rman_sg_nodes/rman_sg_mesh.py | 7 +++++++ rman_translators/rman_mesh_translator.py | 14 +++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/rman_sg_nodes/rman_sg_mesh.py b/rman_sg_nodes/rman_sg_mesh.py index 294b538b..b14f7526 100644 --- a/rman_sg_nodes/rman_sg_mesh.py +++ b/rman_sg_nodes/rman_sg_mesh.py @@ -12,6 +12,13 @@ def __init__(self, rman_scene, sg_node, db_name): self.subdiv_scheme = 'none' self.is_multi_material = False self.multi_material_children = [] + self.sg_mesh = None + + def __del__(self): + if self.rman_scene.rman_render.rman_running and self.rman_scene.rman_render.sg_scene: + with self.rman_scene.rman.SGManager.ScopedEdit(self.rman_scene.sg_scene): + self.rman_scene.sg_scene.DeleteDagNode(self.sg_mesh) + super().__del__() @property def matrix_world(self): diff --git a/rman_translators/rman_mesh_translator.py b/rman_translators/rman_mesh_translator.py index ac1a1629..ea3c6042 100644 --- a/rman_translators/rman_mesh_translator.py +++ b/rman_translators/rman_mesh_translator.py @@ -309,8 +309,10 @@ def _get_subd_tags_(self, ob, mesh, primvar): def export(self, ob, db_name): - sg_node = self.rman_scene.sg_scene.CreateMesh(db_name) + sg_node = self.rman_scene.sg_scene.CreateGroup(db_name) rman_sg_mesh = RmanSgMesh(self.rman_scene, sg_node, db_name) + rman_sg_mesh.sg_mesh = self.rman_scene.sg_scene.CreateMesh('') + rman_sg_mesh.sg_node.AddChild(rman_sg_mesh.sg_mesh) if self.rman_scene.do_motion_blur: rman_sg_mesh.is_transforming = object_utils.is_transforming(ob) @@ -323,7 +325,7 @@ def export_deform_sample(self, rman_sg_mesh, ob, time_sample, sg_node=None): mesh = None mesh = ob.to_mesh() if not sg_node: - sg_node = rman_sg_mesh.sg_node + sg_node = rman_sg_mesh.sg_mesh primvar = sg_node.GetPrimVars() P = mesh_utils.get_mesh_points_(mesh) npoints = len(P) @@ -374,7 +376,7 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): return True if not sg_node: - sg_node = rman_sg_mesh.sg_node + sg_node = rman_sg_mesh.sg_mesh rman_sg_mesh.is_subdiv = object_utils.is_subdmesh(ob) use_smooth_normals = getattr(ob.data.renderman, 'rman_smoothnormals', False) @@ -390,7 +392,13 @@ def update(self, ob, rman_sg_mesh, input_mesh=None, sg_node=None): rman_sg_mesh.nverts = 0 rman_sg_mesh.is_transforming = False rman_sg_mesh.is_deforming = False + rman_sg_mesh.sg_node.RemoveChild(rman_sg_mesh.sg_mesh) return None + + # double check that sg_mesh has been added + # as a child + if rman_sg_mesh.sg_node.GetNumChildren() < 1: + rman_sg_mesh.sg_node.AddChild(rman_sg_mesh.sg_mesh) npolys = len(nverts) npoints = len(P) From 341311e0d658f94fc7748b7dc9e24ded4d642b6e Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 13 Apr 2023 09:08:05 -0700 Subject: [PATCH 274/278] Another bug fix for objects disappearing during IPR. Add to clear_instances list even if the RmanUpdate's do_clear_instances is set to False. We don't want another RmanUpdate to come along later and clear the instances afterwards. --- rman_scene_sync.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rman_scene_sync.py b/rman_scene_sync.py index 78eeb021..dcd4cadf 100644 --- a/rman_scene_sync.py +++ b/rman_scene_sync.py @@ -858,12 +858,18 @@ def check_instances(self, batch_mode=False): clear_instances.append(rman_sg_node) if not self.rman_scene.check_visibility(instance): - # This instance is not visible in the viewport. Don't + # This instance is not visible in the viewport. Don't # add an instance of it continue self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) else: + if rman_sg_node not in clear_instances: + # this might be a bit werid, but we don't want another RmanUpdate + # instance to clear the instances afterwards, so we add to the + # clear_instances list + clear_instances.append(rman_sg_node) + # simply grab the existing instance and update the transform and/or material rman_sg_group = self.rman_scene.get_rman_sg_instance(instance, rman_sg_node, instance_parent, psys, create=False) if rman_sg_group: @@ -888,6 +894,9 @@ def check_instances(self, batch_mode=False): self.rman_scene.attach_particle_material(psys.settings, instance_parent, ob_eval, rman_sg_group) else: self.rman_scene.attach_material(ob_eval, rman_sg_group) + else: + # Didn't get an rman_sg_group. Do a full instance export. + self.rman_scene.export_instance(ob_eval, instance, rman_sg_node, rman_type, instance_parent, psys) if not batch_mode: if rman_type == 'LIGHT': From 7ee85024116955edafcf219a308863f34b86c42b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 13 Apr 2023 15:40:10 -0700 Subject: [PATCH 275/278] Update change log --- changelog.txt | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/changelog.txt b/changelog.txt index 9e636ddc..605f26b8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,77 @@ +v25.0 April 17, 2023 + +New Features: +* You can now use the Qt version of the preset browser and texture manager, which +matches what is available in the other RenderMan DCC plugins. To use the Qt version, +go to the addon preferences and change UI Framework from Native to Qt +* Blender's "Persistent Data" option is now supported. This is mostly useful in batch rendering +where for subsequent frames only differences in the scene are calculated, rather than +re-generating the whole scene. Note, this option is off by default. +* For external/batch rendering using Blender Batch style, there is a new Frame Chunking option, +which specifies how many frames each blender process will render (previously, each +blender process would only do one frame). +* We now auto load the Blender's builtin Node Arrange addon. This allows for auto +arranging nodes in the shader graph. Two of the operators have been added to the RenderMan +right click context menu. + +Changes: +* Scene translation time has been improved. In some cases, as much as 27% speed improvement +has been seen. +* IPR to "it" is no longer a separate setting. To IPR to "it", right click in the Viewport, +and go to RenderMan->Render->IPR to it. +* Support for fluid meshes has been added. +* We now export two new user attributes: "user:blender_is_instance" and "user:blender_instance_uv". +If "user:blender_is_instance" is set to 1, this indicates that the current object is an instance. +* For subdivision meshes, you can now select a face map to act as holes for your mesh (note, +this does not work in XPU). +* Socket names for terminal nodes (ex: RenderMan Material, Bxdf etc) have been renamed. For example, +old name (Bxdf), new name (bxdf_in). This is to match the names used in our other DCC plugins. +* Editing attributes or options during IPR should be faster. +* For meshes, exporting of the tangent and bitangent vectors is now off by default. +* The default Subsurface Model for PxrSurface has changed from "Jensen Dipole" to +"Exponential Path Traced". If you would like to revert the default back to "Jensen Dipole", +you can create an override json file for PxrSurface. The json file would look something like this: + +{ + "name": "PxrSurface.args", + "params": [ + { + "name": "subsurfaceType", + "default": 0, + "always_write": true + } + ] +} +See Customizing Blender in the docs on how to install this override file. + +* The clapboard icon in the viewport will now first display an option to either render in +the viewport or to "it" +* For float ramps, you can now select the interpolation type to be used +* You can now change the zoom factor for the Enhance operator in the addon preferences +* We now draw the texture map for gobo and cookie light filters, if the draw textured lights option +is turned on +* The External Rendering panel has been renamed to Batch Rendering +* We now use "it" for the preview image task for our batch render jobs, instead of "sho". +* Volumes are now added to the "global volume aggregate" by default. If you want to turn off this +behavior, turn off the "Global Volume Aggregate" checkbox on volume object properties. + +Bug Fixes: +* Fixed issues with instancing using geometry nodes. +* Fixed issue where channels in the stylized output were in random order +* Fixed issue where the FOV was not correct when the camera sensor width was not default, +and viewport was in perspective mode +* Fixed issue where using the cookie or gobo light filters was slow in the viewport +* Fixed numerous issues with group nodes in the shader graph. +* Frame step setting should not be respected when batch rendering in RIB mode. +* Fixed issues with light linking when a light or an object are no longer in the scene. +* Fixed issue where normals and vectors were not editable in the shader graph. +* Fixed issue where the viewport render progress bar color was incorrect. +* "it" inspector was displaying the wrong min/max samples for IPR +* Various fixes for the package scene operator +* Fix the viewport drawing of gobo and cookie light filters +* Fixed a bug where UDIM textures from linked collections was not working + + v24.4 April 22, 2022 Changes: From 28e3b3e26b4acf3f421656c1f2396997089ad353 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Thu, 13 Apr 2023 16:14:55 -0700 Subject: [PATCH 276/278] Fix lama node upgrade script. We weren't copying all of the param values. Also, fixed the socket names in the cycle nodes convert script to use the new lama names. --- rfb_utils/upgrade_utils.py | 58 ++++++++++++++++++++++++--- rman_cycles_convert/cycles_convert.py | 26 ++++++------ 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/rfb_utils/upgrade_utils.py b/rfb_utils/upgrade_utils.py index 09543afc..409ae2aa 100644 --- a/rfb_utils/upgrade_utils.py +++ b/rfb_utils/upgrade_utils.py @@ -114,8 +114,9 @@ def upgrade_250_1(scene): ''' def copy_param(old_node, new_node, old_nm, new_nm): - if old_node.inputs[old_nm].is_linked: - connected_socket = old_node.inputs[old_nm].links[0].from_socket + socket = old_node.inputs.get(old_nm, None) + if socket and socket.is_linked: + connected_socket = socket.links[0].from_socket nt.links.new(connected_socket, new_node.inputs[new_nm]) else: setattr(new_node, new_nm, getattr(n, old_nm)) @@ -129,35 +130,80 @@ def copy_param(old_node, new_node, old_nm, new_nm): new_node = None if n.bl_label == 'LamaDiffuse': new_node = nt.nodes.new('LamaDiffuseBxdfNode') + nms = ['color', 'normal'] copy_param(n, new_node, 'color', 'diffuseColor') copy_param(n, new_node, 'normal', 'diffuseNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaSheen': new_node = nt.nodes.new('LamaSheenBxdfNode') + nms = ['color', 'normal'] copy_param(n, new_node, 'color', 'sheenColor') copy_param(n, new_node, 'normal', 'sheenNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaConductor': new_node = nt.nodes.new('LamaConductorBxdfNode') - copy_param(n, new_node, 'normal', 'conductorNormal') + nms = ['normal'] + copy_param(n, new_node, 'normal', 'conductorNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaDielectric': new_node = nt.nodes.new('LamaDielectricBxdfNode') + nms = ['normal'] copy_param(n, new_node, 'normal', 'dielectricNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaEmission': new_node = nt.nodes.new('LamaEmissionBxdfNode') - copy_param(n, new_node, 'color', 'emissionColor') + nms = ['color'] + copy_param(n, new_node, 'color', 'emissionColor') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaGeneralizedSchlick': new_node = nt.nodes.new('LamaGeneralizedSchlickBxdfNode') - copy_param(n, new_node, 'normal', 'genSchlickNormal') + nms = ['normal'] + copy_param(n, new_node, 'normal', 'genSchlickNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaSSS': new_node = nt.nodes.new('LamaSSSBxdfNode') + nms = ['color', 'normal'] copy_param(n, new_node, 'color', 'sssColor') copy_param(n, new_node, 'normal', 'sssNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaTranslucent': new_node = nt.nodes.new('LamaTranslucentBxdfNode') + nms = ['color', 'normal'] copy_param(n, new_node, 'color', 'translucentColor') copy_param(n, new_node, 'normal', 'translucentNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) elif n.bl_label == 'LamaTricolorSSS': new_node = nt.nodes.new('LamaTricolorSSSBxdfNode') - copy_param(n, new_node, 'normal', 'sssNormal') + nms = ['normal'] + copy_param(n, new_node, 'normal', 'sssNormal') + for prop_name, meta in n.prop_meta.items(): + if prop_name in nms: + continue + copy_param(n, new_node, prop_name, prop_name) if new_node: new_node.location[0] = n.location[0] diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index 0f44e0c0..6701c987 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -712,10 +712,10 @@ def convert_diffuse_bsdf(nt, node, rman_node): inputs = node.inputs rman_node.name = 'diffuse_bsdf' - convert_cycles_input(nt, inputs['Color'], rman_node, "color") + convert_cycles_input(nt, inputs['Color'], rman_node, "diffuseColor") convert_cycles_input(nt, inputs['Roughness'], rman_node, "roughness") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "diffuseNormal") def convert_glossy_bsdf(nt, node, rman_node): inputs = node.inputs @@ -725,7 +725,7 @@ def convert_glossy_bsdf(nt, node, rman_node): convert_cycles_input(nt, inputs['Roughness'], rman_node, "roughness") convert_cycles_input( - nt, inputs['Normal'], rman_node, "normal") + nt, inputs['Normal'], rman_node, "conductorNormal") if type(node).__class__ == 'ShaderNodeBsdfAnisotropic': convert_cycles_input( @@ -741,7 +741,7 @@ def convert_glass_bsdf(nt, node, rman_node): rman_node, "roughness") convert_cycles_input(nt, inputs['IOR'], rman_node, "IOR") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "conductorNormal") def convert_refraction_bsdf(nt, node, rman_node): @@ -753,7 +753,7 @@ def convert_refraction_bsdf(nt, node, rman_node): rman_node, "roughness") convert_cycles_input(nt, inputs['IOR'], rman_node, "IOR") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "dielectricNormal") def convert_transparent_bsdf(nt, node, rman_node): @@ -766,35 +766,35 @@ def convert_transparent_bsdf(nt, node, rman_node): def convert_translucent_bsdf(nt, node, rman_node): inputs = node.inputs convert_cycles_input(nt, inputs['Color'], rman_node, "reflectionTint") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "translucentNormal") def convert_sss_bsdf(nt, node, rman_node): inputs = node.inputs rman_node.name = 'sss_bsdf' - convert_cycles_input(nt, inputs['Color'], rman_node, "color") + convert_cycles_input(nt, inputs['Color'], rman_node, "sssColor") convert_cycles_input(nt, inputs['Radius'], rman_node, "radius") convert_cycles_input(nt, inputs['Scale'], rman_node, "scale") convert_cycles_input(nt, inputs['IOR'], rman_node, "IOR") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "sssNormal") def convert_velvet_bsdf(nt, node, rman_node): inputs = node.inputs rman_node.name = 'velvet_bsdf' - convert_cycles_input(nt, inputs['Color'], rman_node, "color") - convert_cycles_input(nt, inputs['Normal'], rman_node, "normal") + convert_cycles_input(nt, inputs['Color'], rman_node, "sheenColor") + convert_cycles_input(nt, inputs['Normal'], rman_node, "sheenNormal") def convert_emission_bsdf(nt, node, rman_node): inputs = node.inputs rman_node.name = 'emission_bsdf' - convert_cycles_input(nt, inputs['Color'], rman_node, "color") + convert_cycles_input(nt, inputs['Color'], rman_node, "emissionColor") if not node.inputs['Color'].is_linked and not node.inputs['Strength']: - emission_color = getattr(rman_node, 'color') + emission_color = getattr(rman_node, 'emissionColor') emission_color = inputs['Strength'] * emission_color - setattr(rman_node, 'color', emission_color) + setattr(rman_node, 'emissionColor', emission_color) def convert_hair_bsdf(nt, node, rman_node): inputs = node.inputs From 42a97ff78229ac97e940f780f43d9ed358bb857b Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 14 Apr 2023 07:15:31 -0700 Subject: [PATCH 277/278] Another typo in the cycles_convert code when dealing with glass (conductorNormal -> dielectricNormal) --- rman_cycles_convert/cycles_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index 6701c987..f9c56f74 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -741,7 +741,7 @@ def convert_glass_bsdf(nt, node, rman_node): rman_node, "roughness") convert_cycles_input(nt, inputs['IOR'], rman_node, "IOR") - convert_cycles_input(nt, inputs['Normal'], rman_node, "conductorNormal") + convert_cycles_input(nt, inputs['Normal'], rman_node, "dielectricNormal") def convert_refraction_bsdf(nt, node, rman_node): From c63ff945d235ae15cfa941027728643371f79a09 Mon Sep 17 00:00:00 2001 From: Ian Hsieh Date: Fri, 14 Apr 2023 07:38:19 -0700 Subject: [PATCH 278/278] More typos in the cycles_convert code. This time in the principle bsdf conversion. --- rman_cycles_convert/cycles_convert.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rman_cycles_convert/cycles_convert.py b/rman_cycles_convert/cycles_convert.py index f9c56f74..7054be56 100644 --- a/rman_cycles_convert/cycles_convert.py +++ b/rman_cycles_convert/cycles_convert.py @@ -557,9 +557,9 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): diffuse_node.location[0] -= 1280.0 nodes_list.append(diffuse_node) - nt.links.new(rman_node.outputs["out_baseColor"], diffuse_node.inputs["color"]) + nt.links.new(rman_node.outputs["out_baseColor"], diffuse_node.inputs["diffuseColor"]) nt.links.new(rman_node.outputs["out_roughness"], diffuse_node.inputs["roughness"]) - nt.links.new(rman_node.outputs["out_normal"], diffuse_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_normal"], diffuse_node.inputs["diffuseNormal"]) # subsurface node_name = __BL_NODES_MAP__.get('LamaSSS', None) @@ -569,8 +569,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): sss_node.location[1] -= 240.0 nodes_list.append(sss_node) - nt.links.new(rman_node.outputs["out_sssColor"], sss_node.inputs["color"]) - nt.links.new(rman_node.outputs["out_normal"], sss_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_sssColor"], sss_node.inputs["sssColor"]) + nt.links.new(rman_node.outputs["out_normal"], sss_node.inputs["sssNormal"]) nt.links.new(rman_node.outputs["out_sssRadius"], sss_node.inputs["sssRadius"]) # diff or sss mix @@ -593,8 +593,8 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): sheen_node.location[1] -= 240.0 nodes_list.append(sheen_node) - nt.links.new(rman_node.outputs["out_sheenColor"], sheen_node.inputs["color"]) - nt.links.new(rman_node.outputs["out_normal"], sheen_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_sheenColor"], sheen_node.inputs["sheenColor"]) + nt.links.new(rman_node.outputs["out_normal"], sheen_node.inputs["sheenNormal"]) # diff sheen add node_name = __BL_NODES_MAP__.get('LamaAdd', None) @@ -619,7 +619,7 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): nt.links.new(rman_node.outputs["out_specF0"], specular_node.inputs["reflectivity"]) nt.links.new(rman_node.outputs["out_roughness"], specular_node.inputs["roughness"]) - nt.links.new(rman_node.outputs["out_normal"], specular_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_normal"], specular_node.inputs["conductorNormal"]) nt.links.new(rman_node.outputs["out_anisotropic"], specular_node.inputs["anisotropy"]) nt.links.new(rman_node.outputs["out_anisotropicRotation"], specular_node.inputs["anisotropyRotation"]) @@ -648,7 +648,7 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): nt.links.new(rman_node.outputs["out_baseColor"], transmission_node.inputs["transmissionTint"]) nt.links.new(rman_node.outputs["out_roughness"], transmission_node.inputs["roughness"]) - nt.links.new(rman_node.outputs["out_normal"], transmission_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_normal"], transmission_node.inputs["dielectricNormal"]) # spec transmission add node_name = __BL_NODES_MAP__.get('LamaAdd', None) @@ -672,7 +672,7 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): nodes_list.append(coat_node) nt.links.new(rman_node.outputs["out_clearcoatRoughness"], coat_node.inputs["roughness"]) - nt.links.new(rman_node.outputs["out_clearcoatNormal"], coat_node.inputs["normal"]) + nt.links.new(rman_node.outputs["out_clearcoatNormal"], coat_node.inputs["dielectricNormal"]) # transmission coat add node_name = __BL_NODES_MAP__.get('LamaAdd', None) @@ -694,7 +694,7 @@ def convert_principled_bsdf_to_lama(nt, node, final_mix_node): emission_node.location[1] -= 240.0 nodes_list.append(emission_node) - nt.links.new(rman_node.outputs["out_emissionColor"], emission_node.inputs["color"]) + nt.links.new(rman_node.outputs["out_emissionColor"], emission_node.inputs["emissionColor"]) # final mix node nt.links.new(transmission_coat_add_node.outputs["bxdf_out"], final_mix_node.inputs["material1"])