There is now an ID and GUID map to Blender objects for easy access especially for devs

This commit is contained in:
Dion Moult
2021-02-03 13:47:09 +11:00
parent adf5af947b
commit c6a7dfdadb
6 changed files with 152 additions and 119 deletions
@@ -6,7 +6,7 @@ bpy = sys.modules.get("bpy")
if bpy is not None:
import bpy
import importlib
from . import ui, prop, operator
from . import handler, ui, prop, operator
modules = {
"project": None,
@@ -155,15 +155,16 @@ if bpy is not None:
self.layout.operator(operator.ImportIFC.bl_idname, text="Industry Foundation Classes (.ifc/.ifczip/.ifcxml)")
def on_register(scene):
prop.setDefaultProperties(scene)
handler.setDefaultProperties(scene)
bpy.app.handlers.depsgraph_update_post.remove(on_register)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.app.handlers.depsgraph_update_post.append(on_register)
bpy.app.handlers.load_post.append(prop.setDefaultProperties)
bpy.app.handlers.load_post.append(prop.clearIfcStore)
bpy.app.handlers.load_post.append(handler.setDefaultProperties)
bpy.app.handlers.load_post.append(handler.loadIfcStore)
bpy.app.handlers.save_pre.append(handler.storeIdMap)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
@@ -181,13 +182,14 @@ if bpy is not None:
module.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.append(prop.toggleDecorationsOnLoad)
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.app.handlers.load_post.remove(prop.setDefaultProperties)
bpy.app.handlers.load_post.remove(prop.clearIfcStore)
bpy.app.handlers.load_post.remove(handler.setDefaultProperties)
bpy.app.handlers.load_post.remove(handler.loadIfcStore)
bpy.app.handlers.save_pre.remove(handler.storeIdMap)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
del bpy.types.Scene.BIMProperties
@@ -205,3 +207,4 @@ if bpy is not None:
module.unregister()
bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler)
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
@@ -0,0 +1,102 @@
import bpy
import json
import blenderbim.bim.decoration as decoration
from bpy.app.handlers import persistent
from blenderbim.bim.ifc import IfcStore
@persistent
def loadIfcStore(scene):
IfcStore.file = None
IfcStore.schema = None
props = bpy.context.scene.BIMProperties
IfcStore.id_map = {int(k): bpy.data.objects.get(v) for k, v in json.loads(props.id_map).items()} if props.id_map else {}
IfcStore.guid_map = (
{k: bpy.data.objects.get(v) for k, v in json.loads(props.guid_map).items()} if props.id_map else {}
)
@persistent
def storeIdMap(scene):
bpy.context.scene.BIMProperties.id_map = json.dumps({k: v.name for k, v in IfcStore.id_map.items()})
bpy.context.scene.BIMProperties.guid_map = json.dumps({k: v.name for k, v in IfcStore.guid_map.items()})
@persistent
def setDefaultProperties(scene):
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": False,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "FLAT",
"bpy.context.scene.display.shading.color_type": "SINGLE",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": False,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Shaded"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": True,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "STUDIO",
"bpy.context.scene.display.shading.color_type": "MATERIAL",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": True,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="2")
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
@@ -6,6 +6,8 @@ class IfcStore:
path = ""
file = None
schema = None
id_map = {}
guid_map = {}
pset_template_path = ""
pset_template_file = None
@@ -24,3 +26,16 @@ class IfcStore:
elif IfcStore.schema is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
return IfcStore.schema
@staticmethod
def link_element(element, obj):
IfcStore.id_map[element.id()] = obj
IfcStore.guid_map[element.GlobalId] = obj
obj.BIMObjectProperties.ifc_definition_id = element.id()
@staticmethod
def unlink_element(element, obj=None):
del IfcStore.id_map[element.id()]
del IfcStore.guid_map[element.GlobalId]
if obj:
obj.BIMObjectProperties.ifc_definition_id = 0
@@ -21,7 +21,7 @@ import numpy as np
from pathlib import Path
from itertools import cycle
from datetime import datetime
from . import ifc
from blenderbim.bim.ifc import IfcStore
from . import schema
@@ -300,7 +300,6 @@ class IfcImporter:
self.mesh_shapes = {}
self.time = 0
self.unit_scale = 1
self.added_data = {}
self.native_elements = {}
self.native_data = {}
self.aggregates = {}
@@ -752,7 +751,7 @@ class IfcImporter:
mesh = None
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
IfcStore.link_element(element, obj)
if shape:
m = shape.transformation.matrix.data
@@ -766,7 +765,6 @@ class IfcImporter:
obj.matrix_world = self.apply_blender_offset_to_matrix(self.get_element_matrix(element))
self.add_opening_relation(element, obj)
self.added_data[element.GlobalId] = obj
if element.is_a("IfcOpeningElement"):
obj.display_type = "WIRE"
@@ -996,7 +994,7 @@ class IfcImporter:
def merge_by_class(self):
merge_set = {}
for obj in self.added_data.values():
for obj in IfcStore.id_map.values():
if "/" not in obj.name or "IfcRelAggregates" in obj.users_collection[0].name:
continue
merge_set.setdefault(obj.name.split("/")[0], []).append(obj)
@@ -1004,7 +1002,7 @@ class IfcImporter:
def merge_by_material(self):
merge_set = {}
for obj in self.added_data.values():
for obj in IfcStore.id_map.values():
if "/" not in obj.name or "IfcRelAggregates" in obj.users_collection[0].name:
continue
if not obj.material_slots:
@@ -1030,7 +1028,7 @@ class IfcImporter:
cleaned_material["material"] = bpy.data.materials.new("Merged Material")
cleaned_material["material"].diffuse_color = cleaned_material["diffuse_color"]
for obj in self.added_data.values():
for obj in IfcStore.id_map.values():
if not hasattr(obj, "material_slots") or not obj.material_slots:
continue
for slot in obj.material_slots:
@@ -1054,7 +1052,7 @@ class IfcImporter:
def clean_mesh(self):
obj = None
last_obj = None
for obj in self.added_data.values():
for obj in IfcStore.id_map.values():
if obj.type == "MESH":
obj.select_set(True)
last_obj = obj
@@ -1101,11 +1099,11 @@ class IfcImporter:
)
elif extension.lower() == "ifc":
self.file = ifcopenshell.open(self.ifc_import_settings.input_file)
ifc.IfcStore.file = self.file
IfcStore.file = self.file
def set_ifc_file(self):
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
ifc.IfcStore.path = "self.ifc_import_settings.input_file"
IfcStore.path = "self.ifc_import_settings.input_file"
def calculate_unit_scale(self):
units = self.file.by_type("IfcUnitAssignment")[0]
@@ -1156,7 +1154,6 @@ class IfcImporter:
obj = self.create_product(self.project["ifc"])
if obj:
self.project["blender"].objects.link(obj)
del self.added_data[self.project["ifc"].GlobalId]
def create_spatial_hierarchy(self):
if self.project["ifc"].IsDecomposedBy:
@@ -1179,7 +1176,6 @@ class IfcImporter:
if obj:
self.spatial_structure_elements[global_id]["blender_obj"] = obj
collection.objects.link(obj)
del self.added_data[element.GlobalId]
if element.IsDecomposedBy:
for rel_aggregate in element.IsDecomposedBy:
self.add_related_objects(collection, rel_aggregate.RelatedObjects)
@@ -1205,7 +1201,7 @@ class IfcImporter:
element = rel_aggregate.RelatingObject
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
obj.BIMObjectProperties.ifc_definition_id = element.id()
IfcStore.link_element(element, obj)
self.place_object_in_spatial_tree(element, obj)
collection = bpy.data.collections.new(obj.name)
@@ -1246,11 +1242,15 @@ class IfcImporter:
modifier.object = opening
def place_objects_in_spatial_tree(self):
for global_id, obj in self.added_data.items():
self.place_object_in_spatial_tree(self.file.by_guid(global_id), obj)
for ifc_definition_id, obj in IfcStore.id_map.items():
self.place_object_in_spatial_tree(self.file.by_id(ifc_definition_id), obj)
def place_object_in_spatial_tree(self, element, obj):
if (
if element.is_a("IfcProject"):
return
elif element.GlobalId in self.spatial_structure_elements:
return
elif (
hasattr(element, "ContainedInStructure")
and element.ContainedInStructure
and element.ContainedInStructure[0].RelatingStructure
@@ -1413,9 +1413,7 @@ class IfcImporter:
if self.ifc_import_settings.should_offset_model:
# Potentially, there is a smarter way to do this. See #1047
v_index = cycle((0, 1, 2))
verts = [
v + self.ifc_import_settings.model_offset_coordinates[next(v_index)] for v in verts
]
verts = [v + self.ifc_import_settings.model_offset_coordinates[next(v_index)] for v in verts]
mesh.vertices.foreach_set("co", verts)
else:
mesh.vertices.foreach_set("co", verts)
@@ -1570,7 +1568,6 @@ class IfcImportSettings:
self.ifc_import_filter = "NONE"
self.ifc_selector = ""
@staticmethod
def factory(context, input_file, logger):
scene_bim = context.scene.BIMProperties
@@ -66,7 +66,7 @@ class ReassignClass(bpy.types.Operator):
},
).execute()
obj.name = "{}/{}".format(product.is_a(), "/".join(obj.name.split("/")[1:]))
obj.BIMObjectProperties.ifc_definition_id = int(product.id())
IfcStore.link_element(product, obj)
bpy.context.active_object.BIMObjectProperties.is_reassigning_class = False
return {"FINISHED"}
@@ -108,7 +108,7 @@ class AssignClass(bpy.types.Operator):
},
).execute()
obj.name = "{}/{}".format(product.is_a(), obj.name)
obj.BIMObjectProperties.ifc_definition_id = int(product.id())
IfcStore.link_element(product, obj)
if obj.data:
bpy.ops.bim.add_representation(obj=obj.name, context_id=self.context_id)
@@ -176,12 +176,13 @@ class UnassignClass(bpy.types.Operator):
for obj in objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
IfcStore.unlink_element(product, obj)
usecase = remove_product.Usecase(
self.file,
{"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)},
{"product": product},
)
usecase.execute()
obj.BIMObjectProperties.ifc_definition_id = 0
if "/" in obj.name and obj.name[0:3] == "Ifc":
obj.name = "/".join(obj.name.split("/")[1:])
return {"FINISHED"}
@@ -221,7 +222,7 @@ class CopyClass(bpy.types.Operator):
result = copy_class.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
}).execute()
obj.BIMObjectProperties.ifc_definition_id = result.id()
IfcStore.link_element(result, obj)
if obj.data.users == 1:
bpy.ops.bim.add_representation(obj=obj.name)
else:
+2 -87
View File
@@ -11,7 +11,6 @@ from . import decoration
import bpy
from blenderbim.bim.ifc import IfcStore
from bpy.types import PropertyGroup
from bpy.app.handlers import persistent
from bpy.props import (
PointerProperty,
StringProperty,
@@ -35,83 +34,6 @@ sheets_enum = []
vector_styles_enum = []
@persistent
def clearIfcStore(scene):
IfcStore.file = None
IfcStore.schema = None
@persistent
def setDefaultProperties(scene):
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": False,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "FLAT",
"bpy.context.scene.display.shading.color_type": "SINGLE",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": False,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Shaded"
drawing_style.render_type = "VIEWPORT"
drawing_style.raster_style = json.dumps(
{
"bpy.data.worlds[0].color": (1, 1, 1),
"bpy.context.scene.render.engine": "BLENDER_WORKBENCH",
"bpy.context.scene.render.film_transparent": False,
"bpy.context.scene.display.shading.show_object_outline": True,
"bpy.context.scene.display.shading.show_cavity": True,
"bpy.context.scene.display.shading.cavity_type": "BOTH",
"bpy.context.scene.display.shading.curvature_ridge_factor": 1,
"bpy.context.scene.display.shading.curvature_valley_factor": 1,
"bpy.context.scene.view_settings.view_transform": "Standard",
"bpy.context.scene.display.shading.light": "STUDIO",
"bpy.context.scene.display.shading.color_type": "MATERIAL",
"bpy.context.scene.display.shading.single_color": (1, 1, 1),
"bpy.context.scene.display.shading.show_shadows": True,
"bpy.context.scene.display.shading.shadow_intensity": 0.5,
"bpy.context.scene.display.light_direction": (0.5, 0.5, 0.5),
"bpy.context.scene.view_settings.use_curve_mapping": False,
"space.overlay.show_wireframes": True,
"space.overlay.wireframe_threshold": 0,
"space.overlay.show_floor": False,
"space.overlay.show_axis_x": False,
"space.overlay.show_axis_y": False,
"space.overlay.show_axis_z": False,
"space.overlay.show_object_origins": False,
"space.overlay.show_relationship_lines": False,
}
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Blender Default"
drawing_style.render_type = "DEFAULT"
bpy.ops.bim.save_drawing_style(index="2")
def getDiagramScales(self, context):
global diagram_scales_enum
if (
@@ -238,15 +160,6 @@ def toggleDecorations(self, context):
decoration.DecorationsHandler.uninstall()
@persistent
def toggleDecorationsOnLoad(*args):
toggle = bpy.context.scene.DocProperties.should_draw_decorations
if toggle:
decoration.DecorationsHandler.install(bpy.context)
else:
decoration.DecorationsHandler.uninstall()
def getMaterialPsetNames(self, context):
global materialpsetnames_enum
materialpsetnames_enum.clear()
@@ -479,6 +392,8 @@ class BIMProperties(PropertyGroup):
schema_dir: StringProperty(default=os.path.join(cwd, "schema") + os.path.sep, name="Schema Directory")
data_dir: StringProperty(default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory")
ifc_file: StringProperty(name="IFC File")
id_map: StringProperty(name="ID Map")
guid_map: StringProperty(name="GUID Map")
export_schema: EnumProperty(items=[("IFC4", "IFC4", ""), ("IFC2X3", "IFC2X3", "")], name="IFC Schema")
contexts: EnumProperty(items=getContexts, name="Contexts")
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")