mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-30 12:09:51 +00:00
Light: Optimized radiance rendering workflow
This commit is contained in:
@@ -52,6 +52,7 @@ classes = (
|
|||||||
operator.AddIESLight,
|
operator.AddIESLight,
|
||||||
operator.RemoveIESLight,
|
operator.RemoveIESLight,
|
||||||
operator.SetIESLightObject,
|
operator.SetIESLightObject,
|
||||||
|
operator.CleanupRadianceFiles,
|
||||||
prop.RadianceMaterial,
|
prop.RadianceMaterial,
|
||||||
prop.IESLight,
|
prop.IESLight,
|
||||||
prop.BIMSolarProperties,
|
prop.BIMSolarProperties,
|
||||||
@@ -64,7 +65,7 @@ classes = (
|
|||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Scene.BIMRadianceExporeterProperies = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
|
bpy.types.Scene.BIMRadianceExporterProperties = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
|
||||||
bpy.types.Scene.BIMSolarProperties = bpy.props.PointerProperty(type=prop.BIMSolarProperties)
|
bpy.types.Scene.BIMSolarProperties = bpy.props.PointerProperty(type=prop.BIMSolarProperties)
|
||||||
|
|
||||||
if pyradiance:
|
if pyradiance:
|
||||||
@@ -77,5 +78,5 @@ def register():
|
|||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.BIMRadianceExporeterProperies
|
del bpy.types.Scene.BIMRadianceExporterProperties
|
||||||
del bpy.types.Scene.BIMSolarProperties
|
del bpy.types.Scene.BIMSolarProperties
|
||||||
|
|||||||
@@ -18,17 +18,13 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import bpy
|
||||||
import os
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.light.prop import RadianceExporterProperties, RadianceMaterial, IESLight
|
from bonsai.bim.module.light.prop import RadianceExporterProperties, RadianceMaterial, IESLight
|
||||||
|
|
||||||
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):
|
class MATERIAL_UL_radiance_materials(bpy.types.UIList):
|
||||||
def draw_item(
|
def draw_item(
|
||||||
@@ -89,12 +85,13 @@ class MATERIAL_UL_ies_lights(bpy.types.UIList):
|
|||||||
else:
|
else:
|
||||||
row.label(text="(No file selected)", icon="ERROR")
|
row.label(text="(No file selected)", icon="ERROR")
|
||||||
|
|
||||||
# Target object property (displays object name)
|
# Target: collection or single object
|
||||||
row.prop(item, "target_object", text="", emboss=False)
|
if item.use_collection:
|
||||||
|
row.prop(item, "target_collection", text="", icon="OUTLINER_COLLECTION", emboss=False)
|
||||||
# Object eyedropper picker button (like in modifiers)
|
else:
|
||||||
op = row.operator("radiance.set_ies_light_object", text="", icon="EYEDROPPER")
|
row.prop(item, "target_object", text="", emboss=False)
|
||||||
op.index = index
|
op = row.operator("radiance.set_ies_light_object", text="", icon="EYEDROPPER")
|
||||||
|
op.index = index
|
||||||
|
|
||||||
# Remove button (X icon - negative action)
|
# Remove button (X icon - negative action)
|
||||||
op = row.operator("radiance.remove_ies_light", text="", icon="X")
|
op = row.operator("radiance.remove_ies_light", text="", icon="X")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import calendar
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -43,7 +44,6 @@ from bonsai.bim.module.light.data import SolarData
|
|||||||
from bonsai.bim.module.light.decorator import SolarDecorator
|
from bonsai.bim.module.light.decorator import SolarDecorator
|
||||||
|
|
||||||
sun_position = tool.Blender.get_addon("sun_position")
|
sun_position = tool.Blender.get_addon("sun_position")
|
||||||
now = datetime.datetime.now()
|
|
||||||
|
|
||||||
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
|
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
|
||||||
spectraldb: dict[str, dict[str, str]] = json.load(f)
|
spectraldb: dict[str, dict[str, str]] = json.load(f)
|
||||||
@@ -72,8 +72,8 @@ def update_coordinates(self: "BIMSolarProperties", context: bpy.types.Context) -
|
|||||||
def update_latlong(self: "BIMSolarProperties", context: bpy.types.Context) -> None:
|
def update_latlong(self: "BIMSolarProperties", context: bpy.types.Context) -> None:
|
||||||
sun_props = tool.Blender.get_sun_props()
|
sun_props = tool.Blender.get_sun_props()
|
||||||
assert sun_props
|
assert sun_props
|
||||||
sun_props.longitude = sun_props.longitude
|
sun_props.longitude = self.longitude
|
||||||
sun_props.latitude = sun_props.latitude
|
sun_props.latitude = self.latitude
|
||||||
self["coordinates"] = sun_props.coordinates
|
self["coordinates"] = sun_props.coordinates
|
||||||
update_sun_path(self)
|
update_sun_path(self)
|
||||||
|
|
||||||
@@ -135,6 +135,14 @@ def update_resolution(self: "RadianceExporterProperties", context: bpy.types.Con
|
|||||||
context.scene.render.resolution_y = self.radiance_resolution_y
|
context.scene.render.resolution_y = self.radiance_resolution_y
|
||||||
|
|
||||||
|
|
||||||
|
def update_day(self: "BIMSolarProperties", context: bpy.types.Context) -> None:
|
||||||
|
"""Clamp the day to the valid range for the current month/year."""
|
||||||
|
max_day = calendar.monthrange(self.year, self.month)[1]
|
||||||
|
if self.day > max_day:
|
||||||
|
self["day"] = max_day
|
||||||
|
update_sun_path(self)
|
||||||
|
|
||||||
|
|
||||||
def update_sun_path(self: "BIMSolarProperties", context: Union[bpy.types.Context, None] = None) -> None:
|
def update_sun_path(self: "BIMSolarProperties", context: Union[bpy.types.Context, None] = None) -> None:
|
||||||
if not SolarData.is_loaded:
|
if not SolarData.is_loaded:
|
||||||
SolarData.load()
|
SolarData.load()
|
||||||
@@ -152,9 +160,13 @@ def update_sun_path(self: "BIMSolarProperties", context: Union[bpy.types.Context
|
|||||||
sun_props.sun_distance = self.sun_path_size
|
sun_props.sun_distance = self.sun_path_size
|
||||||
sun_props.latitude = self.latitude
|
sun_props.latitude = self.latitude
|
||||||
sun_props.longitude = self.longitude
|
sun_props.longitude = self.longitude
|
||||||
|
# Clamp day to valid range for the current month/year
|
||||||
|
max_day = calendar.monthrange(self.year, self.month)[1]
|
||||||
|
day = min(self.day, max_day)
|
||||||
|
|
||||||
sun_props.year = self.year
|
sun_props.year = self.year
|
||||||
sun_props.month = self.month
|
sun_props.month = self.month
|
||||||
sun_props.day = self.day
|
sun_props.day = day
|
||||||
sun_props.time = self.hour + (self.minute / 60)
|
sun_props.time = self.hour + (self.minute / 60)
|
||||||
# Preserve IFC sign convention
|
# Preserve IFC sign convention
|
||||||
sun_props.north_offset = self.true_north * -1
|
sun_props.north_offset = self.true_north * -1
|
||||||
@@ -230,7 +242,12 @@ class RadianceMaterial(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class IESLight(PropertyGroup):
|
class IESLight(PropertyGroup):
|
||||||
"""Represents a mapping between an IES light file and a scene Empty object."""
|
"""Represents a mapping between an IES light file and scene Empty objects.
|
||||||
|
|
||||||
|
Supports two targeting modes:
|
||||||
|
- Object mode: target a single Empty object
|
||||||
|
- Collection mode: target all Empty objects in a collection
|
||||||
|
"""
|
||||||
|
|
||||||
ies_file_path: StringProperty(
|
ies_file_path: StringProperty(
|
||||||
name="IES File Path",
|
name="IES File Path",
|
||||||
@@ -238,12 +255,22 @@ class IESLight(PropertyGroup):
|
|||||||
subtype="FILE_PATH",
|
subtype="FILE_PATH",
|
||||||
default="",
|
default="",
|
||||||
)
|
)
|
||||||
|
use_collection: BoolProperty(
|
||||||
|
name="Use Collection",
|
||||||
|
description="Apply this light to all Empty objects in a collection instead of a single object",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
target_object: PointerProperty(
|
target_object: PointerProperty(
|
||||||
type=bpy.types.Object,
|
type=bpy.types.Object,
|
||||||
name="Target Object",
|
name="Target Object",
|
||||||
description="Empty object where the light fixture will be placed",
|
description="Empty object where the light fixture will be placed",
|
||||||
poll=lambda self, obj: obj.type == "EMPTY",
|
poll=lambda self, obj: obj.type == "EMPTY",
|
||||||
)
|
)
|
||||||
|
target_collection: PointerProperty(
|
||||||
|
type=bpy.types.Collection,
|
||||||
|
name="Target Collection",
|
||||||
|
description="Collection of Empty objects where the light fixture will be placed",
|
||||||
|
)
|
||||||
rotation_z: FloatProperty(
|
rotation_z: FloatProperty(
|
||||||
name="Rotation Z",
|
name="Rotation Z",
|
||||||
description="Rotation around Z-axis in degrees (-180 to 180)",
|
description="Rotation around Z-axis in degrees (-180 to 180)",
|
||||||
@@ -288,9 +315,24 @@ class IESLight(PropertyGroup):
|
|||||||
default=0.0,
|
default=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_target_empties(self) -> list[bpy.types.Object]:
|
||||||
|
"""Return all Empty objects this light targets."""
|
||||||
|
if self.use_collection and self.target_collection is not None:
|
||||||
|
return [obj for obj in self.target_collection.all_objects if obj.type == "EMPTY" and not obj.hide_get()]
|
||||||
|
elif not self.use_collection and self.target_object is not None:
|
||||||
|
try:
|
||||||
|
_ = self.target_object.name
|
||||||
|
if not self.target_object.hide_get():
|
||||||
|
return [self.target_object]
|
||||||
|
except ReferenceError:
|
||||||
|
pass
|
||||||
|
return []
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
ies_file_path: str
|
ies_file_path: str
|
||||||
|
use_collection: bool
|
||||||
target_object: Union[bpy.types.Object, None]
|
target_object: Union[bpy.types.Object, None]
|
||||||
|
target_collection: Union[bpy.types.Collection, None]
|
||||||
rotation_z: float
|
rotation_z: float
|
||||||
is_enabled: bool
|
is_enabled: bool
|
||||||
lamp_type: str
|
lamp_type: str
|
||||||
@@ -326,7 +368,10 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
mappings = json.load(f)
|
mappings = json.load(f)
|
||||||
|
|
||||||
for style_id, mapping in mappings.items():
|
for style_id, mapping in mappings.items():
|
||||||
material = self.get_material_mapping(mapping["name"])
|
# Try matching by style_id first, fall back to name
|
||||||
|
material = self.get_material_mapping_by_id(style_id)
|
||||||
|
if material is None:
|
||||||
|
material = self.get_material_mapping(mapping["name"])
|
||||||
if material:
|
if material:
|
||||||
material.style_id = style_id
|
material.style_id = style_id
|
||||||
material.category = mapping["category"]
|
material.category = mapping["category"]
|
||||||
@@ -338,11 +383,14 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
new_material.subcategory = mapping["subcategory"]
|
new_material.subcategory = mapping["subcategory"]
|
||||||
new_material.is_mapped = True
|
new_material.is_mapped = True
|
||||||
|
|
||||||
|
def get_material_mapping_by_id(self, style_id: str) -> Union[RadianceMaterial, None]:
|
||||||
|
return next((item for item in self.materials if item.style_id == style_id), None)
|
||||||
|
|
||||||
def get_material_mapping(self, style_name: str) -> Union[RadianceMaterial, None]:
|
def get_material_mapping(self, style_name: str) -> Union[RadianceMaterial, None]:
|
||||||
return next((item for item in self.materials 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: str, style_name: str, category: str, subcategory: str) -> None:
|
def set_material_mapping(self, style_id: str, style_name: str, category: str, subcategory: str) -> None:
|
||||||
item = self.get_material_mapping(style_name)
|
item = self.get_material_mapping_by_id(style_id) or self.get_material_mapping(style_name)
|
||||||
if item:
|
if item:
|
||||||
item.category = category
|
item.category = category
|
||||||
item.subcategory = subcategory
|
item.subcategory = subcategory
|
||||||
@@ -358,8 +406,12 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
if item.category and item.subcategory
|
if item.category and item.subcategory
|
||||||
}
|
}
|
||||||
|
|
||||||
def unmap_material(self, style_name: str) -> None:
|
def unmap_material(self, style_name: str, style_id: str = "") -> None:
|
||||||
item = self.get_material_mapping(style_name)
|
item = None
|
||||||
|
if style_id:
|
||||||
|
item = self.get_material_mapping_by_id(style_id)
|
||||||
|
if item is None:
|
||||||
|
item = self.get_material_mapping(style_name)
|
||||||
if item:
|
if item:
|
||||||
item.category = ""
|
item.category = ""
|
||||||
item.subcategory = ""
|
item.subcategory = ""
|
||||||
@@ -369,6 +421,14 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
name="Is Exporting", description="Whether the OBJ export is in progress", default=False
|
name="Is Exporting", description="Whether the OBJ export is in progress", default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is_preparing: bpy.props.BoolProperty(
|
||||||
|
name="Is Preparing", description="Whether scene preparation is in progress", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
is_rendering: bpy.props.BoolProperty(
|
||||||
|
name="Is Rendering", description="Whether a Radiance render is in progress", default=False
|
||||||
|
)
|
||||||
|
|
||||||
categories = [
|
categories = [
|
||||||
("Wall", "Wall", ""),
|
("Wall", "Wall", ""),
|
||||||
("Floor", "Floor", ""),
|
("Floor", "Floor", ""),
|
||||||
@@ -436,6 +496,15 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
update=lambda self, context: self.update_ifc_file(context),
|
update=lambda self, context: self.update_ifc_file(context),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ambient_bounces: IntProperty(
|
||||||
|
name="Ambient Bounces",
|
||||||
|
description="Number of indirect light bounces. Higher = more light fills dark areas but slower. "
|
||||||
|
"1 is minimal, 2-3 recommended for IES-only scenes, 4+ for complex interiors",
|
||||||
|
min=0,
|
||||||
|
max=8,
|
||||||
|
default=2,
|
||||||
|
)
|
||||||
|
|
||||||
radiance_quality: EnumProperty(
|
radiance_quality: EnumProperty(
|
||||||
name="Quality",
|
name="Quality",
|
||||||
description="Radiance rendering quality",
|
description="Radiance rendering quality",
|
||||||
@@ -569,6 +638,15 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
default=3.0,
|
default=3.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
false_color_steps: IntProperty(
|
||||||
|
name="Legend Steps",
|
||||||
|
description="Number of divisions on the legend. Contour increment = Scale / Steps. "
|
||||||
|
"E.g. Scale=20, Steps=10 → contours at 2, 4, 6, 8...",
|
||||||
|
min=2,
|
||||||
|
max=50,
|
||||||
|
default=10,
|
||||||
|
)
|
||||||
|
|
||||||
false_color_contour_lines: BoolProperty(
|
false_color_contour_lines: BoolProperty(
|
||||||
name="Enable Contour Lines",
|
name="Enable Contour Lines",
|
||||||
description="Add contour lines to the false color image",
|
description="Add contour lines to the false color image",
|
||||||
@@ -605,6 +683,8 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
is_exporting: bool
|
is_exporting: bool
|
||||||
|
is_preparing: bool
|
||||||
|
is_rendering: bool
|
||||||
category: str
|
category: str
|
||||||
subcategory: str
|
subcategory: str
|
||||||
materials: bpy.types.bpy_prop_collection_idprop[RadianceMaterial]
|
materials: bpy.types.bpy_prop_collection_idprop[RadianceMaterial]
|
||||||
@@ -614,6 +694,7 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
radiance_resolution_y: int
|
radiance_resolution_y: int
|
||||||
output_dir: str
|
output_dir: str
|
||||||
ifc_file: str
|
ifc_file: str
|
||||||
|
ambient_bounces: int
|
||||||
radiance_quality: Literal["LOW", "MEDIUM", "HIGH"]
|
radiance_quality: Literal["LOW", "MEDIUM", "HIGH"]
|
||||||
radiance_detail: Literal["LOW", "MEDIUM", "HIGH"]
|
radiance_detail: Literal["LOW", "MEDIUM", "HIGH"]
|
||||||
radiance_variability: Literal["LOW", "MEDIUM", "HIGH"]
|
radiance_variability: Literal["LOW", "MEDIUM", "HIGH"]
|
||||||
@@ -632,6 +713,7 @@ class RadianceExporterProperties(PropertyGroup):
|
|||||||
use_false_color: bool
|
use_false_color: bool
|
||||||
false_color_label: Literal["fc", "lux", "cd/m2"]
|
false_color_label: Literal["fc", "lux", "cd/m2"]
|
||||||
false_color_scale: float
|
false_color_scale: float
|
||||||
|
false_color_steps: int
|
||||||
false_color_contour_lines: bool
|
false_color_contour_lines: bool
|
||||||
false_color_contour_mode: Literal["WITH_BG", "WITHOUT_BG"]
|
false_color_contour_mode: Literal["WITH_BG", "WITHOUT_BG"]
|
||||||
false_color_multiplier: float
|
false_color_multiplier: float
|
||||||
@@ -662,11 +744,12 @@ class BIMSolarProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
timezone: StringProperty(name="Timezone", default="Etc/GMT")
|
timezone: StringProperty(name="Timezone", default="Etc/GMT")
|
||||||
true_north: FloatProperty(name="True North", min=-pi, max=pi, subtype="ANGLE", update=update_sun_path)
|
true_north: FloatProperty(name="True North", min=-pi, max=pi, subtype="ANGLE", update=update_sun_path)
|
||||||
year: IntProperty(name="Year", min=1, max=9999, default=now.year, update=update_sun_path)
|
# Defaults are static; use the "Now" button (LightSetTimeToNow) to set current time.
|
||||||
month: IntProperty(name="Month", min=1, max=12, default=now.month, update=update_sun_path)
|
year: IntProperty(name="Year", min=1, max=9999, default=2025, update=update_day)
|
||||||
day: IntProperty(name="Date", min=1, max=31, default=now.day, update=update_sun_path)
|
month: IntProperty(name="Month", min=1, max=12, default=1, update=update_day)
|
||||||
hour: IntProperty(name="Hour", min=0, max=23, default=now.hour, update=update_sun_path)
|
day: IntProperty(name="Date", min=1, max=31, default=1, update=update_day)
|
||||||
minute: IntProperty(name="Minute", min=0, max=59, default=now.minute, update=update_sun_path)
|
hour: IntProperty(name="Hour", min=0, max=23, default=12, update=update_sun_path)
|
||||||
|
minute: IntProperty(name="Minute", min=0, max=59, default=0, update=update_sun_path)
|
||||||
sun_position: FloatVectorProperty(name="Sun Position", subtype="XYZ", default=(0, 0, 0))
|
sun_position: FloatVectorProperty(name="Sun Position", subtype="XYZ", default=(0, 0, 0))
|
||||||
sun_path_origin: FloatVectorProperty(name="Sun Path Origin", subtype="XYZ", default=(0, 0, 0))
|
sun_path_origin: FloatVectorProperty(name="Sun Path Origin", subtype="XYZ", default=(0, 0, 0))
|
||||||
sun_path_size: FloatProperty(name="Sun Path Size", min=0.1, default=50, update=update_sun_path)
|
sun_path_size: FloatProperty(name="Sun Path Size", min=0.1, default=50, update=update_sun_path)
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
row = box.row()
|
row = box.row()
|
||||||
row.prop(props, "radiance_variability")
|
row.prop(props, "radiance_variability")
|
||||||
|
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "ambient_bounces")
|
||||||
|
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.prop(props, "output_file_name")
|
row.prop(props, "output_file_name")
|
||||||
|
|
||||||
@@ -140,10 +143,6 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
row = box.row()
|
row = box.row()
|
||||||
row.prop(props, "use_hdr")
|
row.prop(props, "use_hdr")
|
||||||
|
|
||||||
if props.use_hdr:
|
|
||||||
row = box.row()
|
|
||||||
row.prop(props, "choose_hdr_image")
|
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
# Sky Generation Settings
|
# Sky Generation Settings
|
||||||
@@ -179,6 +178,21 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="Selected Light Properties")
|
box.label(text="Selected Light Properties")
|
||||||
|
|
||||||
|
# Target mode toggle
|
||||||
|
row = box.row()
|
||||||
|
row.prop(active_light, "use_collection", text="Target Collection", icon="OUTLINER_COLLECTION")
|
||||||
|
|
||||||
|
# Show the appropriate target picker
|
||||||
|
row = box.row()
|
||||||
|
if active_light.use_collection:
|
||||||
|
row.prop(active_light, "target_collection", text="Collection")
|
||||||
|
if active_light.target_collection:
|
||||||
|
empties = [o for o in active_light.target_collection.all_objects if o.type == "EMPTY"]
|
||||||
|
row = box.row()
|
||||||
|
row.label(text=f"{len(empties)} empty object(s) in collection", icon="INFO")
|
||||||
|
else:
|
||||||
|
row.prop(active_light, "target_object", text="Object")
|
||||||
|
|
||||||
# Rotation Z
|
# Rotation Z
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.prop(active_light, "rotation_z")
|
row.prop(active_light, "rotation_z")
|
||||||
@@ -213,7 +227,10 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="Step 2: Prepare Radiance scene")
|
box.label(text="Step 2: Prepare Radiance scene")
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.operator("scene.prepare_radiance", text="Prepare Scene")
|
if props.is_preparing:
|
||||||
|
row.label(text="Preparing scene...", icon="SORTTIME")
|
||||||
|
else:
|
||||||
|
row.operator("scene.prepare_radiance", text="Prepare Scene")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
@@ -221,8 +238,11 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="Step 3: Run the simulation")
|
box.label(text="Step 3: Run the simulation")
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.operator("render_scene.radiance", text="Radiance Render")
|
if props.is_rendering:
|
||||||
row.enabled = not props.is_exporting
|
row.label(text="Rendering in progress...", icon="RENDER_STILL")
|
||||||
|
else:
|
||||||
|
row.operator("render_scene.radiance", text="Radiance Render")
|
||||||
|
row.enabled = not props.is_exporting
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
@@ -240,6 +260,9 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
row.label(text="Scale Factor")
|
row.label(text="Scale Factor")
|
||||||
row.prop(props, "false_color_scale", text="")
|
row.prop(props, "false_color_scale", text="")
|
||||||
|
|
||||||
|
row = box.row()
|
||||||
|
row.prop(props, "false_color_steps")
|
||||||
|
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.prop(props, "false_color_contour_lines")
|
row.prop(props, "false_color_contour_lines")
|
||||||
|
|
||||||
@@ -259,6 +282,14 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
|||||||
row = box.row()
|
row = box.row()
|
||||||
row.operator("render_scene.false_color_radiance", text="Generate False Color Image")
|
row.operator("render_scene.false_color_radiance", text="Generate False Color Image")
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
box = layout.box()
|
||||||
|
box.label(text="Cleanup")
|
||||||
|
row = box.row()
|
||||||
|
row.operator("radiance.cleanup_files", text="Cleanup Generated Files", icon="TRASH")
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_solar(bpy.types.Panel):
|
class BIM_PT_solar(bpy.types.Panel):
|
||||||
"""Creates a Panel in the render properties window"""
|
"""Creates a Panel in the render properties window"""
|
||||||
@@ -375,7 +406,10 @@ class BIM_PT_solar(bpy.types.Panel):
|
|||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
sun_props = tool.Blender.get_sun_props()
|
sun_props = tool.Blender.get_sun_props()
|
||||||
assert sun_props
|
assert sun_props
|
||||||
row.prop(sun_props.sun_object.data, "energy", text="Sun Intensity")
|
if sun_props.sun_object is not None and sun_props.sun_object.data is not None:
|
||||||
|
row.prop(sun_props.sun_object.data, "energy", text="Sun Intensity")
|
||||||
|
else:
|
||||||
|
row.label(text="Sun object not found. Toggle shadow mode to recreate.", icon="ERROR")
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.view_from_sun", icon="LIGHT_HEMI")
|
row.operator("bim.view_from_sun", icon="LIGHT_HEMI")
|
||||||
|
|||||||
@@ -1845,7 +1845,7 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_radiance_exporter_props(cls) -> RadianceExporterProperties:
|
def get_radiance_exporter_props(cls) -> RadianceExporterProperties:
|
||||||
assert (scene := bpy.context.scene)
|
assert (scene := bpy.context.scene)
|
||||||
return scene.BIMRadianceExporeterProperies # pyright: ignore[reportAttributeAccessIssue]
|
return scene.BIMRadianceExporterProperties # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_fm_props(cls) -> BIMFMProperties:
|
def get_fm_props(cls) -> BIMFMProperties:
|
||||||
|
|||||||
Reference in New Issue
Block a user