From 0dde4102bc438168fd86c8b1e74c275a5d964aca Mon Sep 17 00:00:00 2001 From: Chirag Singh Date: Wed, 24 Jul 2024 12:11:56 +0530 Subject: [PATCH] Added support for mapping material inside the UI. Output file name and file extension taken from input. Plus Fixed some bugs mentioned in #5033 --- .../blenderbim/bim/module/light/__init__.py | 1 + .../blenderbim/bim/module/light/operator.py | 106 +++++++++++++----- .../blenderbim/bim/module/light/prop.py | 46 ++++++++ .../blenderbim/bim/module/light/ui.py | 27 ++++- 4 files changed, 149 insertions(+), 31 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/light/__init__.py b/src/blenderbim/blenderbim/bim/module/light/__init__.py index cb2009806b..febd34c37e 100644 --- a/src/blenderbim/blenderbim/bim/module/light/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/light/__init__.py @@ -27,6 +27,7 @@ classes = ( operator.MoveSunPathTo3DCursor, operator.RadianceRender, operator.ViewFromSun, + operator.RefreshIFCMaterials, prop.BIMSolarProperties, prop.RadianceExporterProperties, ui.BIM_PT_radiance_exporter, diff --git a/src/blenderbim/blenderbim/bim/module/light/operator.py b/src/blenderbim/blenderbim/bim/module/light/operator.py index 533e3751c7..bf250b92d7 100644 --- a/src/blenderbim/blenderbim/bim/module/light/operator.py +++ b/src/blenderbim/blenderbim/bim/module/light/operator.py @@ -35,19 +35,20 @@ import multiprocessing from mathutils import Vector from blenderbim.bim.module.light.data import SolarData +ifc_materials = [] class ExportOBJ(bpy.types.Operator): """Exports the IFC File to OBJ""" bl_idname = "export_scene.radiance" bl_label = "Export" bl_description = "Export the IFC to OBJ" + def execute(self, context): # Get the output directory should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory output_dir = context.scene.radiance_exporter_properties.output_dir - json_file = context.scene.radiance_exporter_properties.json_file context.scene.radiance_exporter_properties.is_exporting = True @@ -69,8 +70,8 @@ class ExportOBJ(bpy.types.Operator): ifc_file_path = context.scene.radiance_exporter_properties.ifc_file ifc_file = ifcopenshell.open(ifc_file_path) - for material in ifc_file.by_type("IfcMaterial"): - self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}") + # for material in ifc_file.by_type("IfcMaterial"): + # self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}") obj_file_path = os.path.join(output_dir, "model.obj") mtl_file_path = os.path.join(output_dir, "model.mtl") @@ -92,10 +93,16 @@ class ExportOBJ(bpy.types.Operator): while True: shape = iterator.get() materials = shape.geometry.materials + # print(shape.geometry) material_ids = shape.geometry.material_ids + # material_names = shape.geometry.material_names + # print(material_ids) + for material in materials: - print(material, dir(material)) - print(material_ids) + # print(material, dir(material)) + # print(material.name) + ifc_materials.append(material.name) + serialiser.write(shape) if not iterator.next(): break @@ -103,6 +110,8 @@ class ExportOBJ(bpy.types.Operator): serialiser.finalize() context.scene.radiance_exporter_properties.is_exporting = False + self.report({'INFO'}, "Exported OBJ file to: {}".format(obj_file_path)) + return {'FINISHED'} @@ -118,14 +127,14 @@ class RadianceRender(bpy.types.Operator): return {'CANCELLED'} # Get the resolution from the user input - resolution_x, resolution_y = self.getResolution(context) - quality = context.scene.radiance_exporter_properties.radiance_quality.upper() - detail = context.scene.radiance_exporter_properties.radiance_detail.upper() - variability = context.scene.radiance_exporter_properties.radiance_variability.upper() - - should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory - output_dir = context.scene.radiance_exporter_properties.output_dir - json_file = context.scene.radiance_exporter_properties.json_file + props = context.scene.radiance_exporter_properties + resolution_x, resolution_y = props.radiance_resolution_x, props.radiance_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 obj_file_path = os.path.join(output_dir, "model.obj") @@ -139,26 +148,33 @@ class RadianceRender(bpy.types.Operator): camera_position, camera_direction = self.get_camera_data(camera) # Material processing - style = [] + # style = [] - with open(obj_file_path, "r") as obj_file: - for line in obj_file: - if line.startswith("usemtl"): - l = line.strip().split(" ") - style.append(l[1]) + # with open(obj_file_path, "r") as obj_file: + # for line in obj_file: + # if line.startswith("usemtl"): + # l = line.strip().split(" ") + # style.append(l[1]) # Check if json file is empty or not - if json_file: - with open(json_file, 'r') as file: + props = context.scene.radiance_exporter_properties + + if props.use_json_file: + # Load data from JSON file + with open(props.json_file, 'r') as file: data = json.load(file) else: - data = {} + # Use in-UI material mappings + data = props.get_mappings_dict() + + print(data) + # Create materials.rad file materials_file = os.path.join(output_dir, "materials.rad") with open(materials_file, "w") as file: - file.write("void plastic white\n0\n0\n5 0.6 0.6 0.6 0 0\n") + file.write("void plastic white\n0\n0\n5 0.8 0.8 0.8 0 0\n") file.write("void plastic blue_plastic\n0\n0\n5 0.1 0.2 0.8 0.05 0.1\n") file.write("void plastic red_plastic\n0\n0\n5 0.8 0.1 0.2 0.05 0.1\n") file.write("void metal silver_metal\n0\n0\n5 0.8 0.8 0.8 0.9 0.1\n") @@ -166,8 +182,13 @@ class RadianceRender(bpy.types.Operator): file.write("void light white_light\n0\n0\n3 1.0 1.0 1.0\n") file.write("void trans olive_trans\n0\n0\n7 0.6 0.7 0.4 0.05 0.05 0.7 0.2\n") - for i in set(style): - file.write("inherit alias " + i + " " + data.get(i, "white") + "\n") + for style_id, radiance_material in data.items(): + print(style_id, radiance_material) + file.write("inherit alias " + style_id + " " + data.get(style_id, "white") + "\n") + # file.write(f"inherit alias {style_id} {radiance_material}\n") + # for i in set(ifc_materials): + # print(i) + # file.write("inherit alias " + i + " " + data.get(i, "white") + "\n") self.report({'INFO'}, "Exported Materials Rad file to: {}".format(materials_file)) @@ -178,7 +199,8 @@ class RadianceRender(bpy.types.Operator): self.report({'INFO'}, "obj2mesh output: {}".format(mesh_file_path)) scene_file = os.path.join(output_dir, "scene.rad") with open(scene_file, "w") as file: - file.write("void mesh model\n1 " + rtm_file_path + "\n0\n0\n") + # file.write("void mesh model\n1 " + rtm_file_path + "\n0\n0\n") + file.write('void mesh model\n1 "' + rtm_file_path + '"\n0\n0\n') self.report({'INFO'}, "Exported Scene file to: {}".format(scene_file)) @@ -197,11 +219,16 @@ class RadianceRender(bpy.types.Operator): image = pr.render(scene, ambbounce=1, resolution=(resolution_x, resolution_y), quality=quality, detail=detail, variability=variability) - raw_hdr_path = os.path.join(output_dir, "raw.hdr") - with open(raw_hdr_path, "wb") as wtr: - wtr.write(image) + output_path = os.path.join(output_dir, f"{output_file_name}.{output_file_format.lower()}") - self.report({'INFO'}, "Radiance rendering completed. Output: {}".format(raw_hdr_path)) + if output_file_format == 'HDR': + with open(output_path, "wb") as wtr: + wtr.write(image) + else: + pass + + + self.report({'INFO'}, "Radiance rendering completed. Output: {}".format(output_path)) return {'FINISHED'} def get_active_camera(self, context): @@ -305,3 +332,22 @@ class ViewFromSun(bpy.types.Operator): props = context.scene.BIMSolarProperties props.hour = props.hour # Just to refresh camera position return {"FINISHED"} + +class RefreshIFCMaterials(bpy.types.Operator): + bl_idname = "bim.refresh_ifc_materials" + bl_label = "Refresh IFC Materials" + bl_description = "Refresh the list of IFC materials for mapping" + + def execute(self, context): + props = context.scene.radiance_exporter_properties + ifc_file = tool.Ifc.get() if props.should_load_from_memory else ifcopenshell.open(props.ifc_file) + + for style in ifc_file.by_type("IfcSurfaceStyle"): + for render_item in style.Styles: + if render_item.is_a("IfcSurfaceStyleRendering"): + style_id = f"IfcSurfaceStyleRendering-{render_item.id()}" + style_name = style.Name or f"Unnamed Style {render_item.id()}" + if not props.get_material_mapping(style_name): + props.add_material_mapping(style_id, style_name) + + return {'FINISHED'} diff --git a/src/blenderbim/blenderbim/bim/module/light/prop.py b/src/blenderbim/blenderbim/bim/module/light/prop.py index 225fbf3621..62d1abf24a 100644 --- a/src/blenderbim/blenderbim/bim/module/light/prop.py +++ b/src/blenderbim/blenderbim/bim/module/light/prop.py @@ -151,12 +151,43 @@ class RadianceExporterProperties(PropertyGroup): if self.ifc_file: self.ifc_file = bpy.path.abspath(self.ifc_file) + def add_material_mapping(self, style_id, style_name): + item = self.material_mappings.add() + item.name = style_name + item["style_id"] = style_id + item["radiance_material"] = "white" # Default to white + + def get_material_mapping(self, style_name): + return next((item for item in self.material_mappings if item.name == style_name), None) + + def set_material_mapping(self, style_id, style_name, radiance_material): + item = self.get_material_mapping(style_name) + if item: + item["radiance_material"] = radiance_material + else: + self.add_material_mapping(style_id, style_name) + self.material_mappings[-1]["radiance_material"] = radiance_material + + def get_mappings_dict(self): + return {item["style_id"]: item["radiance_material"] for item in self.material_mappings} + + use_json_file: BoolProperty( + name="Upload JSON", + description="Toggle between uploading a JSON file and using in-UI material mapping", + default=False + ) + is_exporting: bpy.props.BoolProperty( name="Is Exporting", description="Whether the OBJ export is in progress", default=False ) + material_mappings: bpy.props.CollectionProperty( + type= bpy.types.PropertyGroup, + name="Material Mappings" + ) + should_load_from_memory: BoolProperty( name="Load from Memory", default=False, @@ -226,6 +257,21 @@ class RadianceExporterProperties(PropertyGroup): ], default="MEDIUM", ) + output_file_name: StringProperty( + name="Output File Name", + description="Name of the output image file (without extension)", + default="render" + ) + + output_file_format: EnumProperty( + name="Output File Format", + description="Format of the output image file", + items=[ + ('HDR', "HDR", "High Dynamic Range"), + ], + default='HDR' + ) + class BIMSolarProperties(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/light/ui.py b/src/blenderbim/blenderbim/bim/module/light/ui.py index 4900270856..9c78261807 100644 --- a/src/blenderbim/blenderbim/bim/module/light/ui.py +++ b/src/blenderbim/blenderbim/bim/module/light/ui.py @@ -52,9 +52,25 @@ class BIM_PT_radiance_exporter(bpy.types.Panel): row = layout.row() row.prop(props, "output_dir") + row = layout.row() - row.prop(props, "json_file") + layout.prop(props, "use_json_file") + + if props.use_json_file: + row = layout.row() + row.prop(props, "json_file") + else: + # Material Mappings UI + layout.label(text="Material Mappings:") + for item in props.material_mappings: + row = layout.row(align=True) + row.label(text=item.name) + row.prop(item, '["radiance_material"]', text="") + + layout.operator("bim.refresh_ifc_materials") + + layout.separator() row = layout.row() row.label(text="Resolution") row.prop(props, "radiance_resolution_x", text="X") @@ -71,7 +87,16 @@ class BIM_PT_radiance_exporter(bpy.types.Panel): row = layout.row() row.prop(props, "radiance_variability") + + layout.separator() + + row = layout.row() + row.prop(props, "output_file_name") + row = layout.row() + row.prop(props, "output_file_format") + layout.separator() + row = layout.row() row.operator("export_scene.radiance", text="Export Geometry for Simulation")