mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user