mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
basic update to blender2.8, use of existing mesh if id match,
This commit is contained in:
committed by
Thomas Krijnen
parent
6bac067488
commit
9f7a7a2c5b
@@ -27,42 +27,46 @@
|
|||||||
|
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "IfcBlender",
|
"name": "IfcBlender",
|
||||||
"description": "Import files in the "\
|
"description": "Import files in the "
|
||||||
"Industry Foundation Classes (.ifc) file format",
|
"Industry Foundation Classes (.ifc) file format",
|
||||||
"author": "Thomas Krijnen, IfcOpenShell",
|
"author": "Thomas Krijnen, IfcOpenShell",
|
||||||
"blender": (2, 73, 0),
|
"blender": (2, 80, 0),
|
||||||
"location": "File > Import",
|
"location": "File > Import",
|
||||||
"tracker_url": "https://sourceforge.net/p/ifcopenshell/"\
|
"tracker_url": "https://sourceforge.net/p/ifcopenshell/"
|
||||||
"_list/tickets?source=navbar",
|
"_list/tickets?source=navbar",
|
||||||
"category": "Import-Export"}
|
"category": "Import-Export"}
|
||||||
|
|
||||||
if "bpy" in locals():
|
if "bpy" in locals():
|
||||||
import imp
|
import importlib
|
||||||
if "ifcopenshell" in locals():
|
if "ifcopenshell" in locals():
|
||||||
imp.reload(ifcopenshell)
|
importlib.reload(ifcopenshell)
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||||
from bpy_extras.io_utils import ImportHelper
|
from bpy_extras.io_utils import ImportHelper
|
||||||
|
|
||||||
major,minor = bpy.app.version[0:2]
|
major, minor = bpy.app.version[0:2]
|
||||||
transpose_matrices = minor >= 62
|
transpose_matrices = minor >= 62
|
||||||
|
|
||||||
bpy.types.Object.ifc_id = IntProperty(name="IFC Entity ID",
|
bpy.types.Object.ifc_id = IntProperty(
|
||||||
|
name="IFC Entity ID",
|
||||||
description="The STEP entity instance name")
|
description="The STEP entity instance name")
|
||||||
bpy.types.Object.ifc_guid = StringProperty(name="IFC Entity GUID",
|
bpy.types.Object.ifc_guid = StringProperty(
|
||||||
|
name="IFC Entity GUID",
|
||||||
description="The IFC Globally Unique Identifier")
|
description="The IFC Globally Unique Identifier")
|
||||||
bpy.types.Object.ifc_name = StringProperty(name="IFC Entity Name",
|
bpy.types.Object.ifc_name = StringProperty(
|
||||||
|
name="IFC Entity Name",
|
||||||
description="The optional name attribute")
|
description="The optional name attribute")
|
||||||
bpy.types.Object.ifc_type = StringProperty(name="IFC Entity Type",
|
bpy.types.Object.ifc_type = StringProperty(
|
||||||
|
name="IFC Entity Type",
|
||||||
description="The STEP Datatype keyword")
|
description="The STEP Datatype keyword")
|
||||||
|
|
||||||
|
|
||||||
def import_ifc(filename, use_names, process_relations, blender_booleans):
|
def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||||
from . import ifcopenshell
|
from . import ifcopenshell
|
||||||
from .ifcopenshell import geom as ifcopenshell_geom
|
from .ifcopenshell import geom as ifcopenshell_geom
|
||||||
print("Reading %s..."%bpy.path.basename(filename))
|
print(f"Reading {bpy.path.basename(filename)}...")
|
||||||
settings = ifcopenshell_geom.settings()
|
settings = ifcopenshell_geom.settings()
|
||||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans)
|
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans)
|
||||||
iterator = ifcopenshell_geom.iterator(settings, filename)
|
iterator = ifcopenshell_geom.iterator(settings, filename)
|
||||||
@@ -76,6 +80,11 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
openings = []
|
openings = []
|
||||||
old_progress = -1
|
old_progress = -1
|
||||||
print("Creating geometry...")
|
print("Creating geometry...")
|
||||||
|
collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}")
|
||||||
|
bpy.context.scene.collection.children.link(collection)
|
||||||
|
if process_relations:
|
||||||
|
rel_collection = bpy.data.collections.new("Relations")
|
||||||
|
collection.children.link(rel_collection)
|
||||||
while True:
|
while True:
|
||||||
ob = iterator.get()
|
ob = iterator.get()
|
||||||
|
|
||||||
@@ -86,54 +95,75 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
m = ob.transformation.matrix.data
|
m = ob.transformation.matrix.data
|
||||||
t = ob.type[0:21]
|
t = ob.type[0:21]
|
||||||
nm = ob.name if len(ob.name) and use_names else ob.guid
|
nm = ob.name if len(ob.name) and use_names else ob.guid
|
||||||
|
# MESH CREATION
|
||||||
verts = [[v[i], v[i + 1], v[i + 2]] \
|
|
||||||
for i in range(0, len(v), 3)]
|
|
||||||
faces = [[f[i], f[i + 1], f[i + 2]] \
|
|
||||||
for i in range(0, len(f), 3)]
|
|
||||||
|
|
||||||
# Depending on version, geometry.id will be either int or str
|
# Depending on version, geometry.id will be either int or str
|
||||||
me = bpy.data.meshes.new('mesh-%r' % ob.geometry.id)
|
mesh_name = 'mesh-%r' % ob.geometry.id
|
||||||
me.from_pydata(verts, [], faces)
|
if mesh_name in bpy.data.meshes:
|
||||||
me.validate()
|
me = bpy.data.meshes[mesh_name]
|
||||||
|
else:
|
||||||
|
verts = [[v[i], v[i + 1], v[i + 2]]
|
||||||
|
for i in range(0, len(v), 3)]
|
||||||
|
faces = [[f[i], f[i + 1], f[i + 2]]
|
||||||
|
for i in range(0, len(f), 3)]
|
||||||
|
|
||||||
def add_material(mname, props):
|
me = bpy.data.meshes.new(mesh_name)
|
||||||
if mname in bpy.data.materials:
|
me.from_pydata(verts, [], faces)
|
||||||
mat = bpy.data.materials[mname]
|
me.validate()
|
||||||
mat.use_fake_user = True
|
# MATERIAL CREATION
|
||||||
else:
|
def add_material(mname, props):
|
||||||
mat = bpy.data.materials.new(mname)
|
if mname in bpy.data.materials:
|
||||||
for k,v in props.items():
|
mat = bpy.data.materials[mname]
|
||||||
setattr(mat, k, v)
|
mat.use_fake_user = True
|
||||||
me.materials.append(mat)
|
else:
|
||||||
|
mat = bpy.data.materials.new(mname)
|
||||||
|
for k, v in props.items():
|
||||||
|
if k == 'transparency':
|
||||||
|
mat.blend_method = 'HASHED'
|
||||||
|
mat.use_screen_refraction = True
|
||||||
|
mat.refraction_depth = 0.1
|
||||||
|
mat.use_nodes = True
|
||||||
|
mat.node_tree.nodes["Principled BSDF"].inputs[15].default_value = v
|
||||||
|
else:
|
||||||
|
setattr(mat, k, v)
|
||||||
|
me.materials.append(mat)
|
||||||
|
|
||||||
needs_default = -1 in matids
|
needs_default = -1 in matids
|
||||||
if needs_default: add_material(t, {})
|
if needs_default:
|
||||||
|
add_material(t, {})
|
||||||
|
|
||||||
for mat in mats:
|
for mat in mats:
|
||||||
props = {}
|
props = {}
|
||||||
if mat.has_diffuse: props['diffuse_color'] = mat.diffuse
|
if mat.has_diffuse:
|
||||||
if mat.has_specular: props['specular_color'] = mat.specular
|
props['diffuse_color'] = mat.diffuse
|
||||||
if mat.has_transparency and mat.transparency > 0:
|
if mat.has_specular:
|
||||||
props['alpha'] = 1.0 - mat.transparency
|
props['specular_color'] = mat.specular
|
||||||
props['use_transparency'] = True
|
if mat.has_transparency and mat.transparency > 0:
|
||||||
if mat.has_specularity: props['specular_hardness'] = mat.specularity
|
props['transparency'] = mat.transparency
|
||||||
add_material(mat.name, props)
|
if mat.has_specularity:
|
||||||
|
props['specular_intensity'] = mat.specularity
|
||||||
|
add_material(mat.name, props)
|
||||||
|
|
||||||
|
faces = me.polygons if hasattr(me, 'polygons') else me.faces
|
||||||
|
if len(faces) == len(matids):
|
||||||
|
for face, matid in zip(faces, matids):
|
||||||
|
face.material_index = matid + (1 if needs_default else 0)
|
||||||
|
|
||||||
|
# OBJECT CREATION
|
||||||
bob = bpy.data.objects.new(nm, me)
|
bob = bpy.data.objects.new(nm, me)
|
||||||
mat = mathutils.Matrix(([m[0], m[1], m[2], 0],
|
mat = mathutils.Matrix(([m[0], m[1], m[2], 0],
|
||||||
[m[3], m[4], m[5], 0],
|
[m[3], m[4], m[5], 0],
|
||||||
[m[6], m[7], m[8], 0],
|
[m[6], m[7], m[8], 0],
|
||||||
[m[9], m[10], m[11], 1]))
|
[m[9], m[10], m[11], 1]))
|
||||||
if transpose_matrices: mat.transpose()
|
if transpose_matrices:
|
||||||
|
mat.transpose()
|
||||||
|
|
||||||
if process_relations:
|
if process_relations:
|
||||||
id_to_matrix[ob.id] = mat
|
id_to_matrix[ob.id] = mat
|
||||||
else:
|
else:
|
||||||
bob.matrix_world = mat
|
bob.matrix_world = mat
|
||||||
bpy.context.scene.objects.link(bob)
|
collection.objects.link(bob)
|
||||||
|
|
||||||
bpy.context.scene.objects.active = bob
|
bpy.context.view_layer.objects.active = bob
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
bpy.ops.mesh.normals_make_consistent()
|
bpy.ops.mesh.normals_make_consistent()
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
@@ -143,10 +173,11 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
|
|
||||||
if ob.type == 'IfcSpace' or ob.type == 'IfcOpeningElement':
|
if ob.type == 'IfcSpace' or ob.type == 'IfcOpeningElement':
|
||||||
if not (ob.type == 'IfcOpeningElement' and blender_booleans):
|
if not (ob.type == 'IfcOpeningElement' and blender_booleans):
|
||||||
bob.hide = bob.hide_render = True
|
bob.hide_viewport = bob.hide_render = True
|
||||||
bob.draw_type = 'WIRE'
|
bob.display_type = 'WIRE'
|
||||||
|
|
||||||
if ob.id not in id_to_object: id_to_object[ob.id] = []
|
if ob.id not in id_to_object:
|
||||||
|
id_to_object[ob.id] = []
|
||||||
id_to_object[ob.id].append(bob)
|
id_to_object[ob.id].append(bob)
|
||||||
|
|
||||||
if ob.parent_id > 0:
|
if ob.parent_id > 0:
|
||||||
@@ -155,11 +186,6 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
if blender_booleans and ob.type == 'IfcOpeningElement':
|
if blender_booleans and ob.type == 'IfcOpeningElement':
|
||||||
openings.append(ob.id)
|
openings.append(ob.id)
|
||||||
|
|
||||||
faces = me.polygons if hasattr(me, 'polygons') else me.faces
|
|
||||||
if len(faces) == len(matids):
|
|
||||||
for face, matid in zip(faces, matids):
|
|
||||||
face.material_index = matid + (1 if needs_default else 0)
|
|
||||||
|
|
||||||
progress = iterator.progress() // 2
|
progress = iterator.progress() // 2
|
||||||
if progress > old_progress:
|
if progress > old_progress:
|
||||||
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
||||||
@@ -194,10 +220,11 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
[m[3], m[4], m[5], 0],
|
[m[3], m[4], m[5], 0],
|
||||||
[m[6], m[7], m[8], 0],
|
[m[6], m[7], m[8], 0],
|
||||||
[m[9], m[10], m[11], 1]))
|
[m[9], m[10], m[11], 1]))
|
||||||
if transpose_matrices: mat.transpose()
|
if transpose_matrices:
|
||||||
|
mat.transpose()
|
||||||
id_to_matrix[parent_ob.id] = mat
|
id_to_matrix[parent_ob.id] = mat
|
||||||
|
|
||||||
bpy.context.scene.objects.link(bob)
|
rel_collection.objects.link(bob)
|
||||||
|
|
||||||
bob.ifc_id = parent_ob.id
|
bob.ifc_id = parent_ob.id
|
||||||
bob.ifc_name, bob.ifc_type, bob.ifc_guid = \
|
bob.ifc_name, bob.ifc_type, bob.ifc_guid = \
|
||||||
@@ -220,7 +247,7 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
parent_matrix = id_to_matrix.get(parent_id, None)
|
parent_matrix = id_to_matrix.get(parent_id, None)
|
||||||
for ob in id_to_object[id]:
|
for ob in id_to_object[id]:
|
||||||
if parent_matrix:
|
if parent_matrix:
|
||||||
ob.matrix_local = parent_matrix.inverted() * matrix
|
ob.matrix_local = parent_matrix.inverted() @ matrix
|
||||||
else:
|
else:
|
||||||
ob.matrix_world = matrix
|
ob.matrix_world = matrix
|
||||||
|
|
||||||
@@ -236,7 +263,7 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
|||||||
mod.operation = "DIFFERENCE"
|
mod.operation = "DIFFERENCE"
|
||||||
mod.object = opening_ob
|
mod.object = opening_ob
|
||||||
|
|
||||||
txt = bpy.data.texts.new("%s.log"%bpy.path.basename(filename))
|
txt = bpy.data.texts.new(f"{bpy.path.basename(filename)}.log")
|
||||||
txt.from_string(iterator.getLog())
|
txt.from_string(iterator.getLog())
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -247,42 +274,51 @@ class ImportIFC(bpy.types.Operator, ImportHelper):
|
|||||||
bl_label = "Import .ifc file"
|
bl_label = "Import .ifc file"
|
||||||
|
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
filter_glob = StringProperty(default="*.ifc", options={'HIDDEN'})
|
filter_glob: StringProperty(default="*.ifc", options={'HIDDEN'})
|
||||||
|
|
||||||
use_names = BoolProperty(name="Use entity names",
|
use_names: BoolProperty(name="Use entity names",
|
||||||
description="Use entity names rather than GlobalIds for objects",
|
description="Use entity names rather than "
|
||||||
default=True)
|
"GlobalIds for objects",
|
||||||
process_relations = BoolProperty(name="Process relations",
|
default=True)
|
||||||
description="Convert containment and aggregation" \
|
process_relations: BoolProperty(name="Process relations",
|
||||||
" relations to parenting" \
|
description="Convert containment and "
|
||||||
" (warning: may be slow on large files)",
|
"aggregation relations to parenting"
|
||||||
default=False)
|
" (warning: may be slow on large files)",
|
||||||
blender_booleans = BoolProperty(name="Use Blender booleans",
|
default=False)
|
||||||
description="Use Blender boolean modifiers for opening" \
|
blender_booleans: BoolProperty(name="Use Blender booleans",
|
||||||
" elements",
|
description="Use Blender boolean modifiers "
|
||||||
default=False)
|
"for opening elements",
|
||||||
|
default=False)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if not import_ifc(self.filepath, self.use_names, self.process_relations, self.blender_booleans):
|
if not import_ifc(self.filepath, self.use_names,
|
||||||
|
self.process_relations, self.blender_booleans):
|
||||||
self.report({'ERROR'},
|
self.report({'ERROR'},
|
||||||
'Unable to parse .ifc file or no geometrical entities found'
|
'Unable to parse .ifc file or no geometrical entities found'
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
def menu_func_import(self, context):
|
def menu_func_import(self, context):
|
||||||
self.layout.operator(ImportIFC.bl_idname,
|
self.layout.operator(ImportIFC.bl_idname,
|
||||||
text="Industry Foundation Classes (.ifc)")
|
text="Industry Foundation Classes (.ifc)")
|
||||||
|
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
ImportIFC,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_module(__name__)
|
for cls in classes:
|
||||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
bpy.utils.register_class(cls)
|
||||||
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.utils.unregister_module(__name__)
|
for cls in reversed(classes):
|
||||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
bpy.utils.unregister_class(cls)
|
||||||
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user