bim.save_uv_to_style - experimental operator to save current uv to ifc

it's very limited and covering very little cases now. It works only for IfcPolygonalFaceSets representations that consists of triangles.

Short example - https://imgur.com/a/1AxunSN
This commit is contained in:
Andrej730
2023-11-28 14:41:50 +05:00
parent 3eaea9802f
commit bff9d7b192
3 changed files with 142 additions and 0 deletions
@@ -38,6 +38,7 @@ classes = (
operator.EnableEditingSurfaceStyle,
operator.LoadStyles,
operator.RemoveStyle,
operator.SaveUVToStyle,
operator.SelectByStyle,
operator.UnlinkStyle,
operator.UpdateCurrentStyle,
@@ -26,6 +26,7 @@ import ifcopenshell.util.representation
from pathlib import Path
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.style.data import StylesData, StyleAttributesData
from mathutils import Vector
class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
@@ -691,3 +692,122 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
def color_to_dict(self, x):
return {"Red": x[0], "Green": x[1], "Blue": x[2]}
class SaveUVToStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.save_uv_to_style"
bl_label = "Save UV To Style"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
context = bpy.context
ifc_file = tool.Ifc.get()
material = context.active_object.active_material
obj = context.active_object
# find active style item
style_id = material.BIMMaterialProperties.ifc_style_id
style = ifc_file.by_id(style_id)
def get_active_representation_items():
representation = tool.Geometry.get_active_representation(obj)
representation = ifcopenshell.util.representation.resolve_representation(representation)
items = []
for item in representation.Items:
if item.is_a("IfcMappedItem"):
items.extend(item.MappingSource.MappedRepresentation.Items)
items.append(item)
return items
all_styled_items = set(tool.Style.get_styled_items(style))
active_representation_items = set(get_active_representation_items())
if not active_representation_items.issubset(all_styled_items):
self.report(
{"ERROR"},
"Not all items of the current representation are styled by the active style, not yet supported",
)
return {"CANCELLED"}
if any(not i.is_a("IfcPolygonalFaceSet") for i in active_representation_items):
self.report({"ERROR"}, "One of the items is not IfcPolygonalFaceSet, not yet supported.")
return {"CANCELLED"}
for faceset in active_representation_items:
if any(len(f.CoordIndex) != 3 for f in faceset.Faces):
self.report({"ERROR"}, "One of the facesets is not triangulated, not yet supported.")
return {"CANCELLED"}
if len(active_representation_items) != 1:
self.report({"ERROR"}, "Only 1 faceset item is supported.")
return {"CANCELLED"}
texture_style = tool.Style.get_texture_style(material)
if not texture_style:
self.report({"ERROR"}, "No texture style found, not yet supported.")
return {"CANCELLED"}
# unmap preivously used IfcIndexedTextureMap
# TODO: remove orphaned data?
textures = set(texture_style.Textures)
coords = set()
for texture in textures:
coords.update(texture.IsMappedBy or [])
for coord in coords:
if coord.is_a("IfcIndexedTextureMap") and coord.MappedTo in active_representation_items:
coord.Maps = list(set(coord.Maps) - textures)
mesh = obj.data
uv_indices = []
uv_verts = [uv.vector for uv in mesh.uv_layers.active.uv]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
for faceset in active_representation_items:
# remap the faceset CoordList index to the vertices in blender mesh
coordinates_remap = []
remap_failed = False
ifc_coordinates = faceset.Coordinates.CoordList
for co in ifc_coordinates:
co = Vector(co) * si_conversion
index = next((v.index for v in mesh.vertices if (v.co - co).length_squared < 1e-5), None)
coordinates_remap.append(index)
if index is None:
remap_failed = True
# ifc indices start with 1
remap_verts_to_blender = lambda ifc_verts: [coordinates_remap[i - 1] for i in ifc_verts]
if remap_failed or len(ifc_coordinates) != len(mesh.vertices):
self.report({"ERROR"}, "Mesh vertices doesn't match IFC faceset vertices.")
return {"CANCELLED"}
# safe check as I'm not sure indices in Blender and IFC match
faces_remap = [remap_verts_to_blender(triangle_face.CoordIndex) for triangle_face in faceset.Faces]
for ifc_face_remapped in faces_remap:
for blender_face in mesh.polygons:
blender_face_verts = blender_face.vertices[:]
# assume that blender verts order can be different
if set(blender_face_verts) == set(ifc_face_remapped):
blender_face_loops = blender_face.loop_indices[:]
face_uv_indices = [
blender_face_loops[blender_face_verts.index(i)] + 1 for i in ifc_face_remapped
]
uv_indices.append(face_uv_indices)
break
else:
self.report({"ERROR"}, "Couldn't find matching blender face for ifc face.")
return {"CANCELLED"}
texture_coord = ifc_file.create_entity("IfcIndexedTriangleTextureMap")
texture_coord.Maps = list(textures)
texture_coord.MappedTo = faceset
texture_coord.TexCoordIndex = uv_indices
uv_verts_list = ifc_file.create_entity("IfcTextureVertexList")
uv_verts_list.TexCoordsList = uv_verts
texture_coord.TexCoords = uv_verts_list
self.report({"INFO"}, f"UV saved to the style {style.Name}")
return {"FINISHED"}
+21
View File
@@ -485,3 +485,24 @@ class Style(blenderbim.core.tool.Style):
@classmethod
def change_current_style_type(cls, blender_material, style_type):
blender_material.BIMStyleProperties.active_style_type = style_type
@classmethod
def get_styled_items(cls, style):
ifc_file = tool.Ifc.get()
inverses = list(ifc_file.get_inverse(style))
items = []
while inverses:
inverse = inverses.pop()
if inverse.is_a("IfcPresentationStyleAssignment"):
inverses.extend(ifc_file.get_inverse(inverse))
continue
if not (item := inverse.Item):
continue
if item.is_a("IfcMappedItem"):
items.extend(item.MappingSource.MappedRepresentation.Items)
else:
items.append(item)
return items