From 356a10eac9c9c67d9bef7fe8c8c49f78e8909595 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Sun, 30 Jul 2023 12:34:50 +0200 Subject: [PATCH] Add UV texture support for IfcTessellatedFaceSet (#3523) * Add texture support for IfcIndexedTextureMap * fix description. * Fix posible duplicate coordinate index issue * Clean up code. * revert default clean_mesh. * Fix errors. * change to EXTEND mode when not RepeatS/T * remove tris merge to support mix quads and tris. --- src/blenderbim/blenderbim/bim/import_ifc.py | 48 +++++++++++++++++++++ src/blenderbim/blenderbim/bim/prop.py | 1 + src/blenderbim/blenderbim/tool/loader.py | 17 +++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index bcd1d9b674..812fa3cca5 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -52,6 +52,7 @@ class FileCopy(threading.Thread): class MaterialCreator: def __init__(self, ifc_import_settings, ifc_importer): self.mesh = None + self.obj = None self.materials = {} self.styles = {} self.parsed_meshes = set() @@ -60,6 +61,8 @@ class MaterialCreator: def create(self, element, obj, mesh): self.mesh = mesh + # as ifcopenshell triangulates the mesh, we need to merge it to quads again + self.obj = obj if (hasattr(element, "Representation") and not element.Representation) or ( hasattr(element, "RepresentationMaps") and not element.RepresentationMaps ): @@ -132,10 +135,55 @@ class MaterialCreator: return for style_id in style_ids: material = self.styles[style_id] + ifc_coordinate_id = material.BIMMaterialProperties.ifc_coordinate_id + if ifc_coordinate_id != 0: + self.load_texture_map(tool.Ifc.get().by_id(ifc_coordinate_id)) if self.mesh.materials.find(material.name) == -1: self.mesh.materials.append(material) return True + def load_texture_map(self, coordinates): + # Get a BMesh representation + bm = bmesh.new() + bm.from_mesh(self.mesh) + uv_layer = bm.loops.layers.uv.verify() + + # remap the faceset CoordList index to the vertices in blender mesh + coordinates_remap = [] + for co in coordinates.MappedTo.Coordinates.CoordList: + co = mathutils.Vector(co) + index = next(v.index for v in bm.verts if (v.co - co).length_squared < 1e-5) + coordinates_remap.append(index) + + faces_remap = None + texture_map = None + if coordinates.is_a("IfcIndexedPolygonalTextureMap"): + faces_remap = [[coordinates_remap[i-1] for i in tex_coord_index.TexCoordsOf.CoordIndex] + for tex_coord_index in coordinates.TexCoordIndices] + texture_map = [tex_coord_index.TexCoordIndex for tex_coord_index in coordinates.TexCoordIndices] + elif coordinates.is_a("IfcIndexedTriangleTextureMap"): + faces_remap = [[coordinates_remap[i-1] for i in triangle_face] + for triangle_face in coordinates.MappedTo.CoordIndex] + texture_map = coordinates.TexCoordIndex + + # apply uv to each face + for bface in bm.faces: + face = [loop.vert.index for loop in bface.loops] + # find the corresponding TexCoordIndex by matching ifc faceset with blender face + # remap TexCoordIndex as the loop start may different from blender face + texCoordIndex = next( + [tex_coord_index[face_remap.index(i)] for i in face] + for tex_coord_index, face_remap in zip(texture_map, faces_remap) + if all(i in face_remap for i in face) + ) + # apply uv to each loop + for loop, i in zip(bface.loops, texCoordIndex): + loop[uv_layer].uv = coordinates.TexCoords.TexCoordsList[i-1] + + # Finish up, write the bmesh back to the mesh + bm.to_mesh(self.mesh) + bm.free() + def assign_material_slots_to_faces(self): if "ios_materials" not in self.mesh or not self.mesh["ios_materials"]: return diff --git a/src/blenderbim/blenderbim/bim/prop.py b/src/blenderbim/blenderbim/bim/prop.py index 34596a7b56..7dc0262881 100644 --- a/src/blenderbim/blenderbim/bim/prop.py +++ b/src/blenderbim/blenderbim/bim/prop.py @@ -427,6 +427,7 @@ class BIMMaterialProperties(PropertyGroup): attributes: CollectionProperty(name="Attributes", type=Attribute) # In Blender, a material object can map to an IFC material, IFC surface style, or both ifc_style_id: IntProperty(name="IFC Style ID") + ifc_coordinate_id: IntProperty(name="IFC Coordinate ID") shading_checksum: StringProperty(name="Shading Checksum") diff --git a/src/blenderbim/blenderbim/tool/loader.py b/src/blenderbim/blenderbim/tool/loader.py index 54b48d6732..1b3ca755cc 100644 --- a/src/blenderbim/blenderbim/tool/loader.py +++ b/src/blenderbim/blenderbim/tool/loader.py @@ -308,8 +308,10 @@ class Loader(blenderbim.core.tool.Loader): image = bpy.data.images.load(image_url) node.image = image blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs["Base Color"]) - blender_material.node_tree.links.new(node.outputs[1], bsdf.inputs["Alpha"]) - blender_material.blend_method = "BLEND" + # leave it to default(OPAQUE) when no Transparency defined + if transparency := rendering_style.get("Transparency", None): + blender_material.node_tree.links.new(node.outputs[1], bsdf.inputs["Alpha"]) + blender_material.blend_method = "BLEND" elif reflectance_method == "FLAT": bsdf = tool.Blender.get_material_node(blender_material, "MIX_SHADER") @@ -328,7 +330,16 @@ class Loader(blenderbim.core.tool.Loader): blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs[2]) + # extend the image by repeating pixels on its edges if RepeatS or RepeatT is False + repeat_s = texture.get("RepeatS", True) + repeat_t = texture.get("RepeatT", True) + if node and (not repeat_s or not repeat_t): + node.extension = "EXTEND" + # TODO: add support for texture data not ifc elements + # IsMappedBy could only get with the entity_instance for IFC4/IFC4x3 + if isinstance(texture, dict): + texture = tool.Ifc.get().by_id(texture['id']) if node and getattr(texture, "IsMappedBy", None): coordinates = texture.IsMappedBy[0] coord = blender_material.node_tree.nodes.new(type="ShaderNodeTexCoord") @@ -339,3 +350,5 @@ class Loader(blenderbim.core.tool.Loader): blender_material.node_tree.links.new(coord.outputs["Camera"], node.inputs["Vector"]) else: blender_material.node_tree.links.new(coord.outputs["UV"], node.inputs["Vector"]) + # save the TextureMap id for set uv when set material to mesh + blender_material.BIMMaterialProperties.ifc_coordinate_id = coordinates.id()