Compare commits

...

2 Commits

7 changed files with 1387 additions and 68 deletions
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import ui, prop, operator
from . import ui, prop, operator, list
import stat
from pathlib import Path
@@ -38,10 +38,13 @@ classes = (
operator.RadianceRender,
operator.ViewFromSun,
operator.RefreshIFCMaterials,
operator.UnmapMaterial,
prop.RadianceMaterial,
prop.BIMSolarProperties,
prop.RadianceExporterProperties,
ui.BIM_PT_radiance_exporter,
ui.BIM_PT_solar,
list.MATERIAL_UL_radiance_materials,
)
@@ -0,0 +1,19 @@
import bpy
import json
import os
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
spectraldb = json.load(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"}:
row = layout.row(align=True)
row.prop(item, "name", text="", emboss=False, icon_value=icon)
if item.is_mapped:
row.label(text=f"{item.category} - {item.subcategory}")
op = row.operator("bim.unmap_material", text="", icon="X")
op.material_index = index
else:
row.label(text="Not Mapped (White)")
@@ -1,27 +0,0 @@
[
{
"ceiling": {
"Black Ceiling 1": "void plastic black_ceiling\n0\n0\n5 0.0460 0.0456 0.0463 0.0052 0.2000",
"Black Ceiling 2": "void plastic black_ceiling\n0\n0\n5 0.1116 0.1085 0.0938 0.0000 0.0000",
"Plastic Ceiling Vent E14 526": "void plastic plastic_ceiling_vent_e14_526\n0\n0\n5 0.7384 0.7195 0.6540 0.0110 0.0500",
"Specular Reflective Ceiling Panels": "void plastic specular_reflective_ceiling_panels\n0\n0\n5 0.7708 0.7673 0.6819 0.0741 0.0500"
},
"Door": {
"Wooden Door": "void plastic wooden_door\n0\n0\n5 0.6164 0.4257 0.2156 0.0185 0.2000",
"Wooden Door 2": "void plastic wooden_door_2\n0\n0\n5 0.0403 0.0388 0.0406 0.0121 0.2000",
"White Wooden Door": "void plastic white_wooden_door\n0\n0\n5 0.7893 0.7502 0.6413 0.0080 0.2000",
"Off White Door": "void plastic off_white_door\n0\n0\n5 0.8607 0.8611 0.7852 0.0258 0.2000",
"Bedroom Door": "void plastic bedroom_door\n0\n0\n5 0.2508 0.0782 0.0111 0.0279 0.2000"
},
"Floor": {
"Light Blue Ceramic": "void plastic light_blue_ceramic\n0\n0\n5 0.7477 0.7776 0.7807 0.0323 0.1000",
"Light Grey Ceramic": "void plastic light_grey_ceramic\n0\n0\n5 0.6386 0.5941 0.4982 0.0106 0.2000",
"Purple Carpet": "void plastic purple_carpet\n0\n0\n5 0.0425 0.0179 0.0371 0.0001 0.3000"
},
"Wall": {
"Dirty Dark Pink Painted Wall": "void plastic dirty_dark_pink_painted_wall\n0\n0\n5 0.3533 0.1751 0.1690 0.0000 0.2000",
"Blue Green Painted Walls": "void plastic blue_green_painted_walls\n0\n0\n5 -0.0013 0.2092 0.2601 0.0140 0.2000",
"Red Painted Wall": "void plastic red_painted_wall\n0\n0\n5 0.4621 0.0156 0.0091 0.0396 0.2000"
}
}
]
@@ -38,6 +38,9 @@ from blenderbim.bim.module.light.data import SolarData
ifc_materials = []
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
spectraldb = json.load(f)
class ExportOBJ(bpy.types.Operator):
"""Exports the IFC File to OBJ"""
@@ -159,6 +162,15 @@ class RadianceRender(bpy.types.Operator):
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)
camera = self.get_active_camera(context)
if camera is None:
self.report({"ERROR"}, "No active camera found in the scene. Please add a camera and set it as active.")
@@ -174,9 +186,10 @@ class RadianceRender(bpy.types.Operator):
latitude=latitude,
longitude=longitude,
timezone=timezone,
year=2024,
sunny_with_sun=True,
sunny_without_sun=False,
cloudy=True,
cloudy=False,
ground_reflectance=0.2,
turbidity=3.0,
)
@@ -204,7 +217,7 @@ class RadianceRender(bpy.types.Operator):
# 0
# 4 0 0 -1 180
if choose_hdr_image == "Noon":
if use_hdr and choose_hdr_image == "Noon":
with open(sky_file_path, "w") as f:
f.write(sky_description_str)
@@ -268,14 +281,14 @@ ground_glow source ground
4 0 0 -1 180"""
)
else:
elif not use_hdr:
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 = context.scene.radiance_exporter_properties
@@ -286,20 +299,46 @@ ground_glow source ground
data = props.get_mappings_dict()
materials_file = os.path.join(output_dir, "materials.rad")
written_materials = set()
all_materials = set(ifc_materials)
with open(materials_file, "w") as file:
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")
file.write("void glass clear_glass\n0\n0\n3 0.96 0.96 0.96\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")
# Write default materials
default_materials = [
"void plastic white\n0\n0\n5 0.8 0.8 0.8 0 0\n",
# "void plastic blue_plastic\n0\n0\n5 0.1 0.2 0.8 0.05 0.1\n",
# "void plastic red_plastic\n0\n0\n5 0.8 0.1 0.2 0.05 0.1\n",
# "void metal silver_metal\n0\n0\n5 0.8 0.8 0.8 0.9 0.1\n",
# "void glass clear_glass\n0\n0\n3 0.96 0.96 0.96\n",
# "void light white_light\n0\n0\n3 1.0 1.0 1.0\n",
# "void trans olive_trans\n0\n0\n7 0.6 0.7 0.4 0.05 0.05 0.7 0.2\n",
]
for material in default_materials:
file.write(material)
written_materials.add(material.split()[2]) # Add material name to written set
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")
print(data)
print(default_materials)
self.report({"INFO"}, "Exported Materials Rad file to: {}".format(materials_file))
for style_id in all_materials:
material = next((m for m in props.materials if m.style_id == style_id), None)
if material and material.is_mapped:
category, subcategory = material.category, material.subcategory
if category in spectraldb and subcategory in spectraldb[category]:
material_def = spectraldb[category][subcategory]
material_name = material_def.split()[2]
if material_name not in written_materials:
file.write(material_def + "\n")
written_materials.add(material_name)
file.write(f"inherit alias {style_id} {material_name}\n")
else:
file.write(f"inherit alias {style_id} white\n")
else:
# If the material is not mapped, alias it to white
file.write(f"inherit alias {style_id} white\n")
self.report({"INFO"}, f"Exported Materials Rad file to: {materials_file}")
# Run obj2mesh
rtm_file_path = os.path.join(output_dir, "model.rtm")
@@ -334,7 +373,7 @@ ground_glow source ground
variability=variability,
)
output_hdr_path = os.path.join(output_dir, f"render123.{output_file_format.lower()}")
output_hdr_path = os.path.join(output_dir, f"{output_file_name}.{output_file_format.lower()}")
if output_file_format == "HDR":
with open(output_hdr_path, "wb") as wtr:
@@ -464,12 +503,31 @@ class RefreshIFCMaterials(bpy.types.Operator):
props = context.scene.radiance_exporter_properties
ifc_file = tool.Ifc.get() if props.should_load_from_memory else ifcopenshell.open(props.ifc_file)
props.materials.clear()
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()}"
ifc_materials.append(style_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)
props.add_material_mapping(style_id, style_name)
props.active_material_index = 0 if props.materials else -1
self.report({"INFO"}, f"Refreshed {len(props.materials)} IFC materials")
return {"FINISHED"}
class UnmapMaterial(bpy.types.Operator):
bl_idname = "bim.unmap_material"
bl_label = "Unmap Material"
bl_options = {"REGISTER", "UNDO"}
material_index: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.radiance_exporter_properties
material = props.materials[self.material_index]
props.unmap_material(material.name)
return {"FINISHED"}
@@ -19,6 +19,7 @@
import bpy
import pytz
import tzfpy
import json
import datetime
import blenderbim.tool as tool
from math import radians, pi
@@ -32,10 +33,16 @@ from bpy.props import (
BoolProperty,
CollectionProperty,
)
import os
from bpy.types import PropertyGroup
from blenderbim.bim.module.light.data import SolarData
from blenderbim.bim.module.light.decorator import SolarDecorator
sun_position = tool.Blender.get_sun_position_addon()
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
spectraldb = json.load(f)
def get_sites(self, context):
if not SolarData.is_loaded:
@@ -149,6 +156,14 @@ def update_sun_path():
obj.data.ortho_scale = props.sun_path_size * 2
class RadianceMaterial(PropertyGroup):
name: StringProperty(name="Name")
style_id: StringProperty(name="Style ID")
category: StringProperty(name="Category")
subcategory: StringProperty(name="Subcategory")
is_mapped: BoolProperty(name="Is Mapped", default=False)
class RadianceExporterProperties(PropertyGroup):
def update_json_file(self, context):
@@ -164,24 +179,39 @@ class RadianceExporterProperties(PropertyGroup):
self.ifc_file = bpy.path.abspath(self.ifc_file)
def add_material_mapping(self, style_id, style_name):
item = self.material_mappings.add()
item = self.materials.add()
item.name = style_name
item["style_id"] = style_id
item["radiance_material"] = "white" # Default to white
item.style_id = style_id
item.category = ""
item.subcategory = ""
return item
def get_material_mapping(self, style_name):
return next((item for item in self.material_mappings if item.name == style_name), None)
return next((item for item in self.materials if item.name == style_name), None)
def set_material_mapping(self, style_id, style_name, radiance_material):
def set_material_mapping(self, style_id, style_name, category, subcategory):
item = self.get_material_mapping(style_name)
if item:
item["radiance_material"] = radiance_material
item.category = category
item.subcategory = subcategory
else:
self.add_material_mapping(style_id, style_name)
self.material_mappings[-1]["radiance_material"] = radiance_material
self.materials[-1].category = category
self.materials[-1].subcategory = subcategory
def get_mappings_dict(self):
return {item["style_id"]: item["radiance_material"] for item in self.material_mappings}
return {
item.style_id: (item.category, item.subcategory)
for item in self.materials
if item.category and item.subcategory
}
def unmap_material(self, style_name):
item = self.get_material_mapping(style_name)
if item:
item.category = ""
item.subcategory = ""
item.is_mapped = False
use_json_file: BoolProperty(
name="Upload JSON",
@@ -193,7 +223,47 @@ class RadianceExporterProperties(PropertyGroup):
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")
categories = [
("Wall", "Wall", ""),
("Floor", "Floor", ""),
("Ceiling", "Ceiling", ""),
("Door", "Door", ""),
("Furniture", "Furniture", ""),
("Exterior Floor", "Exterior Floor", ""),
("Others", "Others", ""),
("Exterior Building", "Exterior Building", ""),
("Window Mullion", "Window Mullion", ""),
("PV", "PV", ""),
("Plant", "Plant", ""),
("Exterior", "Exterior", ""),
("Color Swatch", "Color Swatch", ""),
]
def update_material_mapping(self, context):
if self.active_material_index >= 0 and self.active_material_index < len(self.materials):
active_material = self.materials[self.active_material_index]
active_material.category = self.category
active_material.subcategory = self.subcategory
active_material.is_mapped = True
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
)
def get_subcategories(self, context):
if self.category in spectraldb:
return [(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
)
materials: CollectionProperty(type=RadianceMaterial)
active_material_index: IntProperty()
# material_mappings: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup, name="Material Mappings")
# material_mappings: CollectionProperty(type=MaterialMapping)
should_load_from_memory: BoolProperty(
File diff suppressed because it is too large Load Diff
@@ -55,15 +55,30 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
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")
if not props.use_json_file:
row = layout.row()
layout.label(text="Info: Unmapped materials default to white")
row = layout.row()
row.template_list("MATERIAL_UL_radiance_materials", "", props, "materials", props, "active_material_index")
if len(props.materials) > 0:
col = layout.column(align=True)
col.prop(props, "category")
if props.category:
col.prop(props, "subcategory")
if props.active_material_index >= 0 and props.active_material_index < len(props.materials):
active_material = props.materials[props.active_material_index]
if active_material.category and active_material.subcategory:
layout.label(
text=f"Mapped: {active_material.name} to {active_material.category} - {active_material.subcategory}"
)
else:
layout.label(text=f"Select category and subcategory for: {active_material.name}")
row = layout.row()
row.operator("bim.refresh_ifc_materials", text="Refresh IFC Materials")
layout.separator()
row = layout.row()
@@ -95,8 +110,9 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
row = layout.row()
row.prop(props, "use_hdr")
row = layout.row()
row.prop(props, "choose_hdr_image")
if props.use_hdr:
row = layout.row()
row.prop(props, "choose_hdr_image")
row = layout.row()
row.operator("export_scene.radiance", text="Export Geometry for Simulation")