mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 02:07:36 +00:00
Added support for mapping material inside the UI. Output file name and file extension taken from input. Plus Fixed some bugs mentioned in #5033
This commit is contained in:
@@ -27,6 +27,7 @@ classes = (
|
|||||||
operator.MoveSunPathTo3DCursor,
|
operator.MoveSunPathTo3DCursor,
|
||||||
operator.RadianceRender,
|
operator.RadianceRender,
|
||||||
operator.ViewFromSun,
|
operator.ViewFromSun,
|
||||||
|
operator.RefreshIFCMaterials,
|
||||||
prop.BIMSolarProperties,
|
prop.BIMSolarProperties,
|
||||||
prop.RadianceExporterProperties,
|
prop.RadianceExporterProperties,
|
||||||
ui.BIM_PT_radiance_exporter,
|
ui.BIM_PT_radiance_exporter,
|
||||||
|
|||||||
@@ -35,19 +35,20 @@ import multiprocessing
|
|||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from blenderbim.bim.module.light.data import SolarData
|
from blenderbim.bim.module.light.data import SolarData
|
||||||
|
|
||||||
|
ifc_materials = []
|
||||||
|
|
||||||
class ExportOBJ(bpy.types.Operator):
|
class ExportOBJ(bpy.types.Operator):
|
||||||
"""Exports the IFC File to OBJ"""
|
"""Exports the IFC File to OBJ"""
|
||||||
bl_idname = "export_scene.radiance"
|
bl_idname = "export_scene.radiance"
|
||||||
bl_label = "Export"
|
bl_label = "Export"
|
||||||
bl_description = "Export the IFC to OBJ"
|
bl_description = "Export the IFC to OBJ"
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
# Get the output directory
|
# Get the output directory
|
||||||
should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory
|
should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory
|
||||||
output_dir = context.scene.radiance_exporter_properties.output_dir
|
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
|
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_path = context.scene.radiance_exporter_properties.ifc_file
|
||||||
ifc_file = ifcopenshell.open(ifc_file_path)
|
ifc_file = ifcopenshell.open(ifc_file_path)
|
||||||
|
|
||||||
for material in ifc_file.by_type("IfcMaterial"):
|
# for material in ifc_file.by_type("IfcMaterial"):
|
||||||
self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}")
|
# self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}")
|
||||||
|
|
||||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
obj_file_path = os.path.join(output_dir, "model.obj")
|
||||||
mtl_file_path = os.path.join(output_dir, "model.mtl")
|
mtl_file_path = os.path.join(output_dir, "model.mtl")
|
||||||
@@ -92,10 +93,16 @@ class ExportOBJ(bpy.types.Operator):
|
|||||||
while True:
|
while True:
|
||||||
shape = iterator.get()
|
shape = iterator.get()
|
||||||
materials = shape.geometry.materials
|
materials = shape.geometry.materials
|
||||||
|
# print(shape.geometry)
|
||||||
material_ids = shape.geometry.material_ids
|
material_ids = shape.geometry.material_ids
|
||||||
|
# material_names = shape.geometry.material_names
|
||||||
|
# print(material_ids)
|
||||||
|
|
||||||
for material in materials:
|
for material in materials:
|
||||||
print(material, dir(material))
|
# print(material, dir(material))
|
||||||
print(material_ids)
|
# print(material.name)
|
||||||
|
ifc_materials.append(material.name)
|
||||||
|
|
||||||
serialiser.write(shape)
|
serialiser.write(shape)
|
||||||
if not iterator.next():
|
if not iterator.next():
|
||||||
break
|
break
|
||||||
@@ -103,6 +110,8 @@ class ExportOBJ(bpy.types.Operator):
|
|||||||
serialiser.finalize()
|
serialiser.finalize()
|
||||||
context.scene.radiance_exporter_properties.is_exporting = False
|
context.scene.radiance_exporter_properties.is_exporting = False
|
||||||
|
|
||||||
|
self.report({'INFO'}, "Exported OBJ file to: {}".format(obj_file_path))
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@@ -118,14 +127,14 @@ class RadianceRender(bpy.types.Operator):
|
|||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
# Get the resolution from the user input
|
# Get the resolution from the user input
|
||||||
resolution_x, resolution_y = self.getResolution(context)
|
props = context.scene.radiance_exporter_properties
|
||||||
quality = context.scene.radiance_exporter_properties.radiance_quality.upper()
|
resolution_x, resolution_y = props.radiance_resolution_x, props.radiance_resolution_y
|
||||||
detail = context.scene.radiance_exporter_properties.radiance_detail.upper()
|
quality = props.radiance_quality.upper()
|
||||||
variability = context.scene.radiance_exporter_properties.radiance_variability.upper()
|
detail = props.radiance_detail.upper()
|
||||||
|
variability = props.radiance_variability.upper()
|
||||||
should_load_from_memory = context.scene.radiance_exporter_properties.should_load_from_memory
|
output_dir = props.output_dir
|
||||||
output_dir = context.scene.radiance_exporter_properties.output_dir
|
output_file_name = props.output_file_name
|
||||||
json_file = context.scene.radiance_exporter_properties.json_file
|
output_file_format = props.output_file_format
|
||||||
|
|
||||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
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)
|
camera_position, camera_direction = self.get_camera_data(camera)
|
||||||
|
|
||||||
# Material processing
|
# Material processing
|
||||||
style = []
|
# style = []
|
||||||
|
|
||||||
with open(obj_file_path, "r") as obj_file:
|
# with open(obj_file_path, "r") as obj_file:
|
||||||
for line in obj_file:
|
# for line in obj_file:
|
||||||
if line.startswith("usemtl"):
|
# if line.startswith("usemtl"):
|
||||||
l = line.strip().split(" ")
|
# l = line.strip().split(" ")
|
||||||
style.append(l[1])
|
# style.append(l[1])
|
||||||
|
|
||||||
|
|
||||||
# Check if json file is empty or not
|
# Check if json file is empty or not
|
||||||
if json_file:
|
props = context.scene.radiance_exporter_properties
|
||||||
with open(json_file, 'r') as file:
|
|
||||||
|
if props.use_json_file:
|
||||||
|
# Load data from JSON file
|
||||||
|
with open(props.json_file, 'r') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
else:
|
else:
|
||||||
data = {}
|
# Use in-UI material mappings
|
||||||
|
data = props.get_mappings_dict()
|
||||||
|
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
|
||||||
# Create materials.rad file
|
# Create materials.rad file
|
||||||
materials_file = os.path.join(output_dir, "materials.rad")
|
materials_file = os.path.join(output_dir, "materials.rad")
|
||||||
with open(materials_file, "w") as file:
|
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 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 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")
|
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 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")
|
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):
|
for style_id, radiance_material in data.items():
|
||||||
file.write("inherit alias " + i + " " + data.get(i, "white") + "\n")
|
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))
|
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))
|
self.report({'INFO'}, "obj2mesh output: {}".format(mesh_file_path))
|
||||||
scene_file = os.path.join(output_dir, "scene.rad")
|
scene_file = os.path.join(output_dir, "scene.rad")
|
||||||
with open(scene_file, "w") as file:
|
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))
|
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),
|
image = pr.render(scene, ambbounce=1, resolution=(resolution_x, resolution_y),
|
||||||
quality=quality, detail=detail, variability=variability)
|
quality=quality, detail=detail, variability=variability)
|
||||||
|
|
||||||
raw_hdr_path = os.path.join(output_dir, "raw.hdr")
|
output_path = os.path.join(output_dir, f"{output_file_name}.{output_file_format.lower()}")
|
||||||
with open(raw_hdr_path, "wb") as wtr:
|
|
||||||
wtr.write(image)
|
|
||||||
|
|
||||||
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'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def get_active_camera(self, context):
|
def get_active_camera(self, context):
|
||||||
@@ -305,3 +332,22 @@ class ViewFromSun(bpy.types.Operator):
|
|||||||
props = context.scene.BIMSolarProperties
|
props = context.scene.BIMSolarProperties
|
||||||
props.hour = props.hour # Just to refresh camera position
|
props.hour = props.hour # Just to refresh camera position
|
||||||
return {"FINISHED"}
|
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'}
|
||||||
|
|||||||
@@ -151,12 +151,43 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
if self.ifc_file:
|
if self.ifc_file:
|
||||||
self.ifc_file = bpy.path.abspath(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(
|
is_exporting: bpy.props.BoolProperty(
|
||||||
name="Is Exporting",
|
name="Is Exporting",
|
||||||
description="Whether the OBJ export is in progress",
|
description="Whether the OBJ export is in progress",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
material_mappings: bpy.props.CollectionProperty(
|
||||||
|
type= bpy.types.PropertyGroup,
|
||||||
|
name="Material Mappings"
|
||||||
|
)
|
||||||
|
|
||||||
should_load_from_memory: BoolProperty(
|
should_load_from_memory: BoolProperty(
|
||||||
name="Load from Memory",
|
name="Load from Memory",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -226,6 +257,21 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
],
|
],
|
||||||
default="MEDIUM",
|
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):
|
class BIMSolarProperties(PropertyGroup):
|
||||||
|
|||||||
@@ -52,9 +52,25 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(props, "output_dir")
|
row.prop(props, "output_dir")
|
||||||
|
|
||||||
|
|
||||||
row = layout.row()
|
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 = layout.row()
|
||||||
row.label(text="Resolution")
|
row.label(text="Resolution")
|
||||||
row.prop(props, "radiance_resolution_x", text="X")
|
row.prop(props, "radiance_resolution_x", text="X")
|
||||||
@@ -71,7 +87,16 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(props, "radiance_variability")
|
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 = layout.row()
|
||||||
row.operator("export_scene.radiance", text="Export Geometry for Simulation")
|
row.operator("export_scene.radiance", text="Export Geometry for Simulation")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user