mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
1. Added a Json File having SpectralDB Materials. 2. Implemented UIList: Lists down the materials to be mapped with. 3. By default Not mapped material is white. 4. Added a Drop down for category and Subcategory from where the user can select the material from spectralDB 5. Corrected hardcoded render123.hdr as highlighted by Manu in #5033
This commit is contained in:
@@ -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,18 @@
|
||||
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,17 @@ 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 +188,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 +219,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 +283,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 +301,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
|
||||
|
||||
print(data)
|
||||
print(default_materials)
|
||||
|
||||
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")
|
||||
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"}, "Exported Materials Rad file to: {}".format(materials_file))
|
||||
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 +375,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 +505,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,13 @@ 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 +178,35 @@ 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 +218,53 @@ 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,29 @@ 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 +109,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")
|
||||
|
||||
Reference in New Issue
Block a user