From 79230b334128d2d380f5c6bf0d1698ed96208220 Mon Sep 17 00:00:00 2001 From: Chirag Singh Date: Sat, 31 Aug 2024 20:20:08 +0530 Subject: [PATCH] Added prop with search, render in 3 steps --- .../bonsai/bim/module/light/__init__.py | 3 + src/bonsai/bonsai/bim/module/light/list.py | 7 +- .../bonsai/bim/module/light/operator.py | 179 ++++++++++++++---- src/bonsai/bonsai/bim/module/light/prop.py | 6 +- src/bonsai/bonsai/bim/module/light/ui.py | 88 ++++++--- 5 files changed, 211 insertions(+), 72 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/light/__init__.py b/src/bonsai/bonsai/bim/module/light/__init__.py index 04bd22a80c..f976f2d6d3 100644 --- a/src/bonsai/bonsai/bim/module/light/__init__.py +++ b/src/bonsai/bonsai/bim/module/light/__init__.py @@ -43,6 +43,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/list.py b/src/bonsai/bonsai/bim/module/light/list.py index 0e44168948..b0dbd141cb 100644 --- a/src/bonsai/bonsai/bim/module/light/list.py +++ b/src/bonsai/bonsai/bim/module/light/list.py @@ -27,12 +27,11 @@ with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f: class MATERIAL_UL_radiance_materials(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - if self.layout_type in {'DEFAULT', 'COMPACT'}: + if self.layout_type in {"DEFAULT", "COMPACT"}: row = layout.row(align=True) - - # Adding color preview or glass icon + if item.category == "Glass": - row.label(icon='SHADING_TEXTURE') + row.label(icon="SHADING_TEXTURE") else: color_rect = row.row() color_rect.prop(item, "color", text="") diff --git a/src/bonsai/bonsai/bim/module/light/operator.py b/src/bonsai/bonsai/bim/module/light/operator.py index db36741993..8e7b7897e1 100644 --- a/src/bonsai/bonsai/bim/module/light/operator.py +++ b/src/bonsai/bonsai/bim/module/light/operator.py @@ -43,6 +43,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) @@ -118,12 +120,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): if pr is None: @@ -143,11 +153,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}") @@ -165,6 +176,21 @@ class RadianceRender(bpy.types.Operator): sun_props = context.scene.BIMSolarProperties sun_pos_props = context.scene.sun_pos_properties sky_file_path = os.path.join(output_dir, "sky.rad") + # latitude = sun_props.latitude + # longitude = sun_props.longitude + # month = sun_props.month + # day = sun_props.day + # hour = sun_props.hour + # minute = sun_props.minute + + # print("Sun Properties:") + # print("Latitude: ", latitude) + # print("Longitude: ", longitude) + # print("Timezone: ", timezone) + # print("Month: ", month) + # print("Day: ", day) + # print("Hour: ", hour) + # print("Minute: ", minute) print("Setting up camera...") if props.use_active_camera: @@ -182,6 +208,15 @@ class RadianceRender(bpy.types.Operator): print(f"Camera position: {camera_position}") print(f"Camera direction: {camera_direction}") + # azimuth, elevation = sun_position.sun_calc.get_sun_coordinates( + # sun_pos_props.time, + # sun_pos_props.latitude, + # sun_pos_props.longitude, + # -sun_pos_props.UTC_zone, + # sun_pos_props.month, + # sun_pos_props.day, + # sun_pos_props.year, + # ) dt = datetime(sun_pos_props.year, sun_props.month, sun_props.day, sun_props.hour, sun_props.minute) @@ -193,11 +228,11 @@ class RadianceRender(bpy.types.Operator): longitude=sun_props.longitude, year=sun_pos_props.year, timezone=-int(sun_props.UTC_zone), - sunny_with_sun=False, - sunny_without_sun=False, - cloudy=False, - ground_reflectance=0.2, - turbidity=3.0, + # sunny_with_sun=True, + # sunny_without_sun=False, + # cloudy=False, + # ground_reflectance=0.2, + # turbidity=3.0, ) sky_description_str = sky_description.decode("utf-8") @@ -296,9 +331,9 @@ ground_glow source ground # 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 = context.scene.radiance_exporter_properties + # props = context.scene.radiance_exporter_properties - data = props.get_mappings_dict() + # data = props.get_mappings_dict() materials_file = os.path.join(output_dir, "materials.rad") written_materials = set() @@ -351,6 +386,7 @@ ground_glow source ground self.report({"INFO"}, "Exported Scene file to: {}".format(scene_file)) print("Setting up Radiance scene...") + scene = pr.Scene("ascene") material_path = os.path.join(output_dir, "materials.rad") @@ -391,7 +427,36 @@ 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() + image = pr.render( scene, ambbounce=1, @@ -421,29 +486,19 @@ ground_glow source ground self.report({"INFO"}, "Radiance rendering completed. Output: {}".format(tiff_path)) return {"FINISHED"} - 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_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): - 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 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): @@ -540,19 +595,19 @@ class RefreshIFCMaterials(bpy.types.Operator): if render_item.is_a("IfcSurfaceStyleRendering"): style_id = f"IfcSurfaceStyleRendering-{render_item.id()}" style_name = style.Name or f"Unnamed Style {render_item.id()}" - + # Extract color color = (1.0, 1.0, 1.0) # Default white if render_item.SurfaceColour: color = ( render_item.SurfaceColour.Red, render_item.SurfaceColour.Green, - render_item.SurfaceColour.Blue + render_item.SurfaceColour.Blue, ) - + material = props.add_material_mapping(style_id, style_name) material.color = color - + material.category = "" material.subcategory = "" material.is_mapped = False @@ -642,3 +697,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 a4c0cb9dbb..ea9a7b0de1 100644 --- a/src/bonsai/bonsai/bim/module/light/prop.py +++ b/src/bonsai/bonsai/bim/module/light/prop.py @@ -17,7 +17,6 @@ # along with Bonsai. If not, see . import bpy -from bonsai.bim.helper import prop_with_search import pytz import tzfpy import json @@ -186,7 +185,8 @@ class RadianceExporterProperties(PropertyGroup): if self.ifc_file: self.ifc_file = bpy.path.abspath(self.ifc_file) - + def get_categories(self, context): + return [(k, k, "") for k in spectraldb.keys()] def add_material_mapping(self, style_id, style_name): item = self.materials.add() @@ -271,7 +271,7 @@ 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): diff --git a/src/bonsai/bonsai/bim/module/light/ui.py b/src/bonsai/bonsai/bim/module/light/ui.py index 3a456badce..5b902ff36e 100644 --- a/src/bonsai/bonsai/bim/module/light/ui.py +++ b/src/bonsai/bonsai/bim/module/light/ui.py @@ -21,6 +21,28 @@ import bonsai.tool as tool 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""" @@ -54,11 +76,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] @@ -77,13 +102,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() @@ -98,34 +116,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