mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
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:
@@ -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"}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user