From 776112bf1de71e94fe5b8feacf2b570a040d7540 Mon Sep 17 00:00:00 2001 From: Chirag Singh Date: Mon, 23 Sep 2024 01:47:09 +0530 Subject: [PATCH] split obj generation separate from other material rad / rtm generation --- .../projects/IFC2X3 Demo Template.ifc | 1 - .../templates/projects/IFC4 Demo Template.ifc | 1 - .../projects/IFC4X3 Demo Template.ifc | 1 - .../bonsai/bim/module/light/__init__.py | 3 + .../bonsai/bim/module/light/operator.py | 196 ++++++++++++------ src/bonsai/bonsai/bim/module/light/prop.py | 17 +- src/bonsai/bonsai/bim/module/light/ui.py | 88 +++++--- 7 files changed, 210 insertions(+), 97 deletions(-) delete mode 120000 src/bonsai/bonsai/bim/data/templates/projects/IFC2X3 Demo Template.ifc delete mode 120000 src/bonsai/bonsai/bim/data/templates/projects/IFC4 Demo Template.ifc delete mode 120000 src/bonsai/bonsai/bim/data/templates/projects/IFC4X3 Demo Template.ifc diff --git a/src/bonsai/bonsai/bim/data/templates/projects/IFC2X3 Demo Template.ifc b/src/bonsai/bonsai/bim/data/templates/projects/IFC2X3 Demo Template.ifc deleted file mode 120000 index 70ed920747..0000000000 --- a/src/bonsai/bonsai/bim/data/templates/projects/IFC2X3 Demo Template.ifc +++ /dev/null @@ -1 +0,0 @@ -../../libraries/IFC2X3 Demo Library.ifc \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/data/templates/projects/IFC4 Demo Template.ifc b/src/bonsai/bonsai/bim/data/templates/projects/IFC4 Demo Template.ifc deleted file mode 120000 index 265cfd34f3..0000000000 --- a/src/bonsai/bonsai/bim/data/templates/projects/IFC4 Demo Template.ifc +++ /dev/null @@ -1 +0,0 @@ -../../libraries/IFC4 Demo Library.ifc \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/data/templates/projects/IFC4X3 Demo Template.ifc b/src/bonsai/bonsai/bim/data/templates/projects/IFC4X3 Demo Template.ifc deleted file mode 120000 index 5242f4c992..0000000000 --- a/src/bonsai/bonsai/bim/data/templates/projects/IFC4X3 Demo Template.ifc +++ /dev/null @@ -1 +0,0 @@ -../../libraries/IFC4X3 Demo Library.ifc \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/module/light/__init__.py b/src/bonsai/bonsai/bim/module/light/__init__.py index 2785bba125..5cae142053 100644 --- a/src/bonsai/bonsai/bim/module/light/__init__.py +++ b/src/bonsai/bonsai/bim/module/light/__init__.py @@ -47,6 +47,9 @@ classes = ( operator.RADIANCE_OT_export_material_mappings, operator.RADIANCE_OT_import_material_mappings, operator.RADIANCE_OT_open_spectraldb, + operator.EnumPropertySearch, + operator.PrepareRadianceScene, + operator.SetEnumProperty, prop.RadianceMaterial, prop.BIMSolarProperties, prop.RadianceExporterProperties, diff --git a/src/bonsai/bonsai/bim/module/light/operator.py b/src/bonsai/bonsai/bim/module/light/operator.py index 4a8d993e0e..3317e8a670 100644 --- a/src/bonsai/bonsai/bim/module/light/operator.py +++ b/src/bonsai/bonsai/bim/module/light/operator.py @@ -41,6 +41,8 @@ from bpy_extras.io_utils import ImportHelper ifc_materials = [] +scene = None + with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f: spectraldb = json.load(f) @@ -61,6 +63,7 @@ class ExportOBJ(bpy.types.Operator): return True def execute(self, context): + # Get the output directory props = tool.Blender.get_radiance_exporter_props() should_load_from_memory = props.should_load_from_memory @@ -125,12 +128,20 @@ class ExportOBJ(bpy.types.Operator): return {"FINISHED"} -class RadianceRender(bpy.types.Operator): - """Radiance Rendering""" +class PrepareRadianceScene(bpy.types.Operator): + bl_idname = "scene.prepare_radiance" + bl_label = "Prepare Radiance Scene" + bl_description = "Prepares the Radiance scene by creating necessary files and setting up the view" - bl_idname = "render_scene.radiance" - bl_label = "Render" - bl_description = "Renders the scene using Radiance" + def get_camera_data(self, camera): + # Get camera position + position = camera.matrix_world.to_translation() + + # Get camera direction + direction = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) + direction.normalize() + + return (position.x, position.y, position.z), (direction.x, direction.y, direction.z) def execute(self, context): print("Starting Radiance rendering process...") @@ -147,11 +158,12 @@ class RadianceRender(bpy.types.Operator): detail = props.radiance_detail.upper() variability = props.radiance_variability.upper() output_dir = props.output_dir - output_file_name = props.output_file_name - output_file_format = props.output_file_format use_hdr = props.use_hdr choose_hdr_image = props.choose_hdr_image + global scene + scene = None + print(f"Resolution: {resolution_x}x{resolution_y}") print(f"Quality: {quality}, Detail: {detail}, Variability: {variability}") print(f"Output directory: {output_dir}") @@ -202,7 +214,6 @@ class RadianceRender(bpy.types.Operator): print(f"Camera position: {camera_position}") print(f"Camera direction: {camera_direction}") - # sun_position = tool.Blender.get_addon("sun_position") # azimuth, elevation = sun_position.sun_calc.get_sun_coordinates( # sun_pos_props.time, # sun_pos_props.latitude, @@ -214,16 +225,18 @@ class RadianceRender(bpy.types.Operator): # ) dt = datetime(sun_pos_props.year, sun_props.month, sun_props.day, sun_props.hour, sun_props.minute) - + print(dt) + print(sun_props.latitude) + print(sun_props.longitude) + print(sun_props.UTC_zone) + print(sun_pos_props.year) sky_description = pr.gensky( dt=dt, - # azimuth=64.1, - # altitude=-26.6, latitude=sun_props.latitude, longitude=sun_props.longitude, year=sun_pos_props.year, timezone=-int(sun_props.UTC_zone), - # sunny_with_sun=False, + # sunny_with_sun=True, # sunny_without_sun=False, # cloudy=False, # ground_reflectance=0.2, @@ -231,7 +244,6 @@ class RadianceRender(bpy.types.Operator): ) sky_description_str = sky_description.decode("utf-8") - # Write all this to file # skyfunc glow sky_glow # 0 @@ -321,14 +333,14 @@ ground_glow source ground with open(sky_file_path, "w") as f: f.write(sky_description_str) f.write("\n") - # f.write("skyfunc glow sky_glow\n0\n0\n4 .9 .9 1.15 0\n") - # f.write("sky_glow source sky\n0\n0\n4 0 0 1 180\n") - # f.write("skyfunc glow ground_glow\n0\n0\n4 1.4 .9 .6 0\n") - # f.write("ground_glow source ground\n0\n0\n4 0 0 -1 180\n") + f.write("skyfunc glow sky_glow\n0\n0\n4 .9 .9 1.15 0\n") + f.write("sky_glow source sky\n0\n0\n4 0 0 1 180\n") + f.write("skyfunc glow ground_glow\n0\n0\n4 1.4 .9 .6 0\n") + f.write("ground_glow source ground\n0\n0\n4 0 0 -1 180\n") props = tool.Blender.get_radiance_exporter_props() - data = props.get_mappings_dict() + # data = props.get_mappings_dict() materials_file = os.path.join(output_dir, "materials.rad") written_materials = set() @@ -422,7 +434,37 @@ ground_glow source ground ) scene.add_view(aview) print("Starting render...") + + self.report({"INFO"}, "Radiance scene prepared successfully.") + return {"FINISHED"} + + +class RadianceRender(bpy.types.Operator): + """Radiance Rendering""" + + bl_idname = "render_scene.radiance" + bl_label = "Render" + bl_description = "Renders the scene using Radiance" + + def execute(self, context): + props = context.scene.radiance_exporter_properties + resolution_x, resolution_y = props.radiance_resolution_x, props.radiance_resolution_y + + context.scene.render.resolution_x = resolution_x + context.scene.render.resolution_y = resolution_y + + quality = props.radiance_quality.upper() + detail = props.radiance_detail.upper() + variability = props.radiance_variability.upper() + output_dir = props.output_dir + output_file_name = props.output_file_name + output_file_format = props.output_file_format + + global scene + start_time = time.time() + cwd_saved = os.getcwd() + os.chdir(output_dir) image = pr.render( scene, ambbounce=1, @@ -432,48 +474,42 @@ ground_glow source ground variability=variability, nproc=multiprocessing.cpu_count(), ) + os.chdir(cwd_saved) end_time = time.time() print(f"Render completed in {end_time - start_time:.2f} seconds") - output_hdr_path = os.path.join(output_dir, f"{output_file_name}.{output_file_format.lower()}") + output_hdr_path = os.path.join(output_dir, f"{output_file_name}.hdr") print(f"Saving HDR output to: {output_hdr_path}") - if output_file_format == "HDR": - with open(output_hdr_path, "wb") as wtr: - wtr.write(image) - else: - pass - print("Applying tone mapping...") - pcond_image = pr.pcond(hdr=output_hdr_path, human=True) + with open(output_hdr_path, "wb") as wtr: + wtr.write(image) + + if output_file_format == "HDR_TIFF": + print("Applying tone mapping...") + pcond_image = pr.pcond(hdr=output_hdr_path, human=True) + + tiff_path = os.path.join(output_dir, f"{output_file_name}.tiff") + print(f"Saving TIFF output to: {tiff_path}") + pr.ra_tiff(inp=pcond_image, out=tiff_path, lzw=True) - tiff_path = os.path.join(output_dir, f"{output_file_name}.tiff") - print(f"Saving TIFF output to: {tiff_path}") - pr.ra_tiff(inp=pcond_image, out=tiff_path, lzw=True) print("Radiance rendering process completed successfully.") - self.report({"INFO"}, "Radiance rendering completed. Output: {}".format(tiff_path)) + self.report({"INFO"}, f"Radiance rendering completed. HDR Output: {output_hdr_path}") + if output_file_format == "HDR_TIFF": + self.report({"INFO"}, f"TIFF Output: {tiff_path}") return {"FINISHED"} - def get_active_camera(self, context): - props = tool.Blender.get_radiance_exporter_props() - if props.use_active_camera: - return context.scene.camera - else: - return props.selected_camera + # def get_active_camera(self, context): + # props = context.scene.radiance_exporter_properties + # if props.use_active_camera: + # return context.scene.camera + # else: + # return props.selected_camera - def get_camera_data(self, camera): - # Get camera position - position = camera.matrix_world.to_translation() - - # Get camera direction - direction = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) - direction.normalize() - - return (position.x, position.y, position.z), (direction.x, direction.y, direction.z) - - def getResolution(self, context): - props = tool.Blender.get_radiance_exporter_props() - resolution_x = props.radiance_resolution_x - resolution_y = props.radiance_resolution_y - return resolution_x, resolution_y + # def getResolution(self, context): + # scene = context.scene + # props = scene.radiance_exporter_properties + # resolution_x = props.radiance_resolution_x + # resolution_y = props.radiance_resolution_y + # return resolution_x, resolution_y def save_obj2mesh_output(inp: Union[bytes, str, Path], output_file: str, **kwargs): @@ -621,27 +657,21 @@ class RefreshIFCMaterials(bpy.types.Operator): style_id = f"IfcSurfaceStyleRendering-{render_item.id()}" style_name = style.Name or f"Unnamed Style {render_item.id()}" - # Extract color and transparency + # Extract color color = (1.0, 1.0, 1.0) # Default white - transparency = 0.0 # Default opaque if render_item.SurfaceColour: color = ( render_item.SurfaceColour.Red, render_item.SurfaceColour.Green, render_item.SurfaceColour.Blue, ) - if hasattr(render_item, "Transparency") and render_item.Transparency is not None: - transparency = render_item.Transparency - # Add material with color material = props.add_material_mapping(style_id, style_name) material.color = color - # If transparency is high, consider it as glass - if transparency > 0.5: - material.category = "Glass" - material.subcategory = "Clear Glass" - material.is_mapped = True + material.category = "" + material.subcategory = "" + material.is_mapped = False props.active_material_index = 0 if props.materials else -1 @@ -729,3 +759,47 @@ class RADIANCE_OT_open_spectraldb(bpy.types.Operator): def execute(self, context): webbrowser.open("https://spectraldb.com") return {"FINISHED"} + + +class EnumPropertySearch(bpy.types.Operator): + bl_idname = "bim.enum_property_search" + bl_label = "Search Enum Property" + bl_options = {"REGISTER", "UNDO"} + + prop_name: bpy.props.StringProperty() + search_term: bpy.props.StringProperty() + + def execute(self, context): + data = context.space_data.context_pointer_get("data") + enum_items = get_enum_items(data, self.prop_name) + filtered_items = [item for item in enum_items if self.search_term.lower() in item[1].lower()] + + def draw_menu(self, context): + layout = self.layout + for item in filtered_items: + props = layout.operator("bim.set_enum_property", text=item[1]) + props.prop_name = self.prop_name + props.enum_value = item[0] + + bpy.context.window_manager.popup_menu(draw_menu, title="Search Results", icon="VIEWZOOM") + return {"FINISHED"} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + self.layout.prop(self, "search_term", text="Search") + + +class SetEnumProperty(bpy.types.Operator): + bl_idname = "bim.set_enum_property" + bl_label = "Set Enum Property" + bl_options = {"REGISTER", "UNDO"} + + prop_name: bpy.props.StringProperty() + enum_value: bpy.props.StringProperty() + + def execute(self, context): + data = context.space_data.context_pointer_get("data") + setattr(data, self.prop_name, self.enum_value) + return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/light/prop.py b/src/bonsai/bonsai/bim/module/light/prop.py index e538ca1bf2..3a229b2be6 100644 --- a/src/bonsai/bonsai/bim/module/light/prop.py +++ b/src/bonsai/bonsai/bim/module/light/prop.py @@ -231,6 +231,9 @@ class RadianceExporterProperties(PropertyGroup): if self.ifc_file: self.ifc_file = bpy.path.abspath(self.ifc_file) + def get_categories(self, context): + return sorted([(k, k, "") for k in spectraldb.keys()]) + def add_material_mapping(self, style_id: str, style_name: str) -> RadianceMaterial: item = self.materials.add() item.name = style_name @@ -314,16 +317,13 @@ class RadianceExporterProperties(PropertyGroup): print(f"Material '{active_material.name}' mapped to {self.category} - {self.subcategory}") category: bpy.props.EnumProperty( - items=categories, name="Category", description="Material category", update=update_material_mapping + items=get_categories, name="Category", description="Material category", update=update_material_mapping ) def get_subcategories(self, context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: - global SUBCATEGORIES_ENUM_ITEMS if self.category in spectraldb: - SUBCATEGORIES_ENUM_ITEMS = [(k, k, "") for k in spectraldb[self.category].keys()] - else: - SUBCATEGORIES_ENUM_ITEMS = [] - return SUBCATEGORIES_ENUM_ITEMS + return sorted([(k, k, "") for k in spectraldb[self.category].keys()]) + return [] subcategory: bpy.props.EnumProperty( items=get_subcategories, name="Subcategory", description="Material subcategory", update=update_material_mapping @@ -391,9 +391,10 @@ class RadianceExporterProperties(PropertyGroup): name="Output File Format", description="Format of the output image file", items=[ - ("HDR", "HDR + Tiff", "High Dynamic Range"), + ("HDR", "HDR", "High Dynamic Range (HDR) file only"), + ("HDR_TIFF", "HDR + Tiff", "High Dynamic Range (HDR) and Tiff files"), ], - default="HDR", + default="HDR_TIFF", ) use_hdr: BoolProperty( diff --git a/src/bonsai/bonsai/bim/module/light/ui.py b/src/bonsai/bonsai/bim/module/light/ui.py index 0f0ffc09cb..aedcc4aa52 100644 --- a/src/bonsai/bonsai/bim/module/light/ui.py +++ b/src/bonsai/bonsai/bim/module/light/ui.py @@ -23,6 +23,28 @@ from typing import TYPE_CHECKING from bonsai.bim.module.light.data import SolarData +def get_enum_items(data, prop_name, context=None): + prop = data.__annotations__[prop_name] + items = prop.keywords.get("items") + if items is None: + return + if not isinstance(items, (list, tuple)): + items = items(data, context or bpy.context) + return items + + +def prop_with_search(layout, data, prop_name, **kwargs): + row = layout.row(align=True) + row.prop(data, prop_name, **kwargs) + try: + if len(get_enum_items(data, prop_name)) > 10: + row.context_pointer_set(name="data", data=data) + op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM") + op.prop_name = prop_name + except TypeError: + pass + + class BIM_PT_radiance_exporter(bpy.types.Panel): """Creates a Panel in the render properties window""" @@ -55,11 +77,14 @@ class BIM_PT_radiance_exporter(bpy.types.Panel): row = layout.row() row.template_list("MATERIAL_UL_radiance_materials", "", props, "materials", props, "active_material_index") row.operator("radiance.open_spectraldb", text="", icon="WORLD") # Globe icon + # layout.use_property_split = True + # layout.use_property_decorate = False + # layout.width_hint = 350 if len(props.materials) > 0: col = layout.column(align=True) - col.prop(props, "category") + prop_with_search(col, props, "category") if props.category: - col.prop(props, "subcategory") + prop_with_search(col, props, "subcategory") if props.active_material_index >= 0 and props.active_material_index < len(props.materials): active_material = props.materials[props.active_material_index] @@ -78,13 +103,6 @@ class BIM_PT_radiance_exporter(bpy.types.Panel): layout.separator() - row = layout.row() - layout.label(text="Step 1: Export geometry for simulation") - row = layout.row() - row.operator("export_scene.radiance", text="Export Geometry for Simulation") - - layout.separator() - box = layout.box() box.label(text="Camera Settings") row = box.row() @@ -99,34 +117,54 @@ class BIM_PT_radiance_exporter(bpy.types.Panel): row.prop(props, "radiance_resolution_x", text="X") row.prop(props, "radiance_resolution_y", text="Y") - row = layout.row() - row.prop(props, "radiance_quality") - - row = layout.row() - row.prop(props, "radiance_detail") - - row = layout.row() - row.prop(props, "radiance_variability") - layout.separator() - row = layout.row() + box = layout.box() + box.label(text="Render Settings") + + row = box.row() + row.prop(props, "radiance_quality") + + row = box.row() + row.prop(props, "radiance_detail") + + row = box.row() + row.prop(props, "radiance_variability") + + row = box.row() row.prop(props, "output_file_name") - row = layout.row() + row = box.row() row.prop(props, "output_file_format") layout.separator() - row = layout.row() + row = box.row() row.prop(props, "use_hdr") if props.use_hdr: - row = layout.row() + row = box.row() row.prop(props, "choose_hdr_image") - row = layout.row() - layout.label(text="Step 2: Run the simulation") - row = layout.row() + # Step 1: Export geometry for simulation + box = layout.box() + box.label(text="Step 1: Export geometry for simulation") + row = box.row() + row.operator("export_scene.radiance", text="Export Geometry for Simulation") + + layout.separator() + + # Step 2: Prepare Radiance scene + box = layout.box() + box.label(text="Step 2: Prepare Radiance scene") + row = box.row() + row.operator("scene.prepare_radiance", text="Prepare Scene") + + layout.separator() + + # Step 3: Run the simulation + box = layout.box() + box.label(text="Step 3: Run the simulation") + row = box.row() row.operator("render_scene.radiance", text="Radiance Render") row.enabled = not props.is_exporting