mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-17 22:11:36 +00:00
UV coordinates for triangulated face sets are now saved in IFC.
This commit is contained in:
@@ -188,8 +188,9 @@ class UpdateRepresentation(bpy.types.Operator):
|
|||||||
"geometry": obj.data,
|
"geometry": obj.data,
|
||||||
"coordinate_offset": coordinate_offset,
|
"coordinate_offset": coordinate_offset,
|
||||||
"total_items": max(1, len(obj.material_slots)),
|
"total_items": max(1, len(obj.material_slots)),
|
||||||
"should_force_faceted_brep": context.scene.BIMGeometryProperties.should_force_faceted_brep,
|
"should_force_faceted_brep": tool.Geometry.should_force_faceted_brep(),
|
||||||
"should_force_triangulation": context.scene.BIMGeometryProperties.should_force_triangulation,
|
"should_force_triangulation": tool.Geometry.should_force_triangulation(),
|
||||||
|
"should_generate_uvs": tool.Geometry.should_generate_uvs(obj),
|
||||||
"ifc_representation_class": self.ifc_representation_class,
|
"ifc_representation_class": self.ifc_representation_class,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ def add_representation(
|
|||||||
total_items=geometry.get_total_representation_items(obj),
|
total_items=geometry.get_total_representation_items(obj),
|
||||||
should_force_faceted_brep=geometry.should_force_faceted_brep(),
|
should_force_faceted_brep=geometry.should_force_faceted_brep(),
|
||||||
should_force_triangulation=geometry.should_force_triangulation(),
|
should_force_triangulation=geometry.should_force_triangulation(),
|
||||||
|
should_generate_uvs=geometry.should_generate_uvs(obj),
|
||||||
ifc_representation_class=ifc_representation_class,
|
ifc_representation_class=ifc_representation_class,
|
||||||
profile_set_usage=profile_set_usage,
|
profile_set_usage=profile_set_usage,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ class Geometry:
|
|||||||
def run_style_add_style(cls, obj=None): pass
|
def run_style_add_style(cls, obj=None): pass
|
||||||
def should_force_faceted_brep(cls): pass
|
def should_force_faceted_brep(cls): pass
|
||||||
def should_force_triangulation(cls): pass
|
def should_force_triangulation(cls): pass
|
||||||
|
def should_generate_uvs(cls, obj): pass
|
||||||
def should_use_presentation_style_assignment(cls): pass
|
def should_use_presentation_style_assignment(cls): pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -298,6 +298,15 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
def should_force_triangulation(cls):
|
def should_force_triangulation(cls):
|
||||||
return bpy.context.scene.BIMGeometryProperties.should_force_triangulation
|
return bpy.context.scene.BIMGeometryProperties.should_force_triangulation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def should_generate_uvs(cls, obj):
|
||||||
|
for slot in obj.material_slots:
|
||||||
|
if slot.material and slot.material.use_nodes:
|
||||||
|
for node in slot.material.node_tree.nodes:
|
||||||
|
if node.type == "TEX_COORD" and node.outputs['UV'].links:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def should_use_presentation_style_assignment(cls):
|
def should_use_presentation_style_assignment(cls):
|
||||||
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class TestAddRepresentation:
|
|||||||
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
|
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
|
||||||
geometry.should_force_faceted_brep().should_be_called().will_return(False)
|
geometry.should_force_faceted_brep().should_be_called().will_return(False)
|
||||||
geometry.should_force_triangulation().should_be_called().will_return(True)
|
geometry.should_force_triangulation().should_be_called().will_return(True)
|
||||||
|
geometry.should_generate_uvs("obj").should_be_called().will_return(True)
|
||||||
ifc.run(
|
ifc.run(
|
||||||
"geometry.add_representation",
|
"geometry.add_representation",
|
||||||
context="context",
|
context="context",
|
||||||
@@ -54,6 +55,7 @@ class TestAddRepresentation:
|
|||||||
total_items=1,
|
total_items=1,
|
||||||
should_force_faceted_brep=False,
|
should_force_faceted_brep=False,
|
||||||
should_force_triangulation=True,
|
should_force_triangulation=True,
|
||||||
|
should_generate_uvs=True,
|
||||||
ifc_representation_class="ifc_representation_class",
|
ifc_representation_class="ifc_representation_class",
|
||||||
profile_set_usage="profile_set_usage",
|
profile_set_usage="profile_set_usage",
|
||||||
).should_be_called().will_return("representation")
|
).should_be_called().will_return("representation")
|
||||||
@@ -109,6 +111,7 @@ class TestAddRepresentation:
|
|||||||
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
|
geometry.get_total_representation_items("obj").should_be_called().will_return(1)
|
||||||
geometry.should_force_faceted_brep().should_be_called().will_return(False)
|
geometry.should_force_faceted_brep().should_be_called().will_return(False)
|
||||||
geometry.should_force_triangulation().should_be_called().will_return(True)
|
geometry.should_force_triangulation().should_be_called().will_return(True)
|
||||||
|
geometry.should_generate_uvs("obj").should_be_called().will_return(True)
|
||||||
ifc.run(
|
ifc.run(
|
||||||
"geometry.add_representation",
|
"geometry.add_representation",
|
||||||
context="context",
|
context="context",
|
||||||
@@ -118,6 +121,7 @@ class TestAddRepresentation:
|
|||||||
total_items=1,
|
total_items=1,
|
||||||
should_force_faceted_brep=False,
|
should_force_faceted_brep=False,
|
||||||
should_force_triangulation=True,
|
should_force_triangulation=True,
|
||||||
|
should_generate_uvs=True,
|
||||||
ifc_representation_class="ifc_representation_class",
|
ifc_representation_class="ifc_representation_class",
|
||||||
profile_set_usage="profile_set_usage",
|
profile_set_usage="profile_set_usage",
|
||||||
).should_be_called().will_return("representation")
|
).should_be_called().will_return("representation")
|
||||||
|
|||||||
@@ -462,6 +462,33 @@ class TestShouldForceTriangulation(NewFile):
|
|||||||
assert subject.should_force_triangulation() is result
|
assert subject.should_force_triangulation() is result
|
||||||
|
|
||||||
|
|
||||||
|
class TestShouldGenerateUVs(NewFile):
|
||||||
|
def test_needs_mesh_data(self):
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
assert subject.should_generate_uvs(obj) is False
|
||||||
|
|
||||||
|
def test_needs_nodes(self):
|
||||||
|
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||||
|
material = bpy.data.materials.new("Material")
|
||||||
|
obj.data.materials.append(material)
|
||||||
|
material.use_nodes = False
|
||||||
|
assert subject.should_generate_uvs(obj) is False
|
||||||
|
|
||||||
|
def test_needs_texture_coordinates_with_a_uv_output(self):
|
||||||
|
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||||
|
material = bpy.data.materials.new("Material")
|
||||||
|
obj.data.materials.append(material)
|
||||||
|
material.use_nodes = True
|
||||||
|
|
||||||
|
bsdf = material.node_tree.nodes["Principled BSDF"]
|
||||||
|
node = material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
material.node_tree.links.new(bsdf.inputs["Base Color"], node.outputs["Color"])
|
||||||
|
|
||||||
|
coords = material.node_tree.nodes.new(type="ShaderNodeTexCoord")
|
||||||
|
material.node_tree.links.new(node.inputs["Vector"], coords.outputs["UV"])
|
||||||
|
assert subject.should_generate_uvs(obj) is True
|
||||||
|
|
||||||
|
|
||||||
class TestShouldUsePresentationStyleAssignment(NewFile):
|
class TestShouldUsePresentationStyleAssignment(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
result = bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
result = bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class Usecase:
|
|||||||
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
||||||
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
||||||
"should_force_triangulation": False, # If we should force triangulation for meshes
|
"should_force_triangulation": False, # If we should force triangulation for meshes
|
||||||
|
"should_generate_uvs": False, # If UV coordinates should also be generated
|
||||||
# Possible IFC representation classes:
|
# Possible IFC representation classes:
|
||||||
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
|
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
|
||||||
# IfcExtrudedAreaSolid/IfcCircleProfileDef
|
# IfcExtrudedAreaSolid/IfcCircleProfileDef
|
||||||
@@ -505,16 +506,43 @@ class Usecase:
|
|||||||
|
|
||||||
def create_triangulated_face_set(self):
|
def create_triangulated_face_set(self):
|
||||||
ifc_raw_items = [None] * self.settings["total_items"]
|
ifc_raw_items = [None] * self.settings["total_items"]
|
||||||
|
if self.settings["should_generate_uvs"]:
|
||||||
|
ifc_raw_uv_items = [None] * self.settings["total_items"]
|
||||||
for i, value in enumerate(ifc_raw_items):
|
for i, value in enumerate(ifc_raw_items):
|
||||||
ifc_raw_items[i] = []
|
ifc_raw_items[i] = []
|
||||||
|
if self.settings["should_generate_uvs"]:
|
||||||
|
ifc_raw_uv_items[i] = []
|
||||||
for polygon in self.settings["geometry"].polygons:
|
for polygon in self.settings["geometry"].polygons:
|
||||||
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
ifc_raw_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
[v + 1 for v in polygon.vertices]
|
[v + 1 for v in polygon.vertices]
|
||||||
)
|
)
|
||||||
|
if self.settings["should_generate_uvs"]:
|
||||||
|
ifc_raw_uv_items[polygon.material_index % self.settings["total_items"]].append(
|
||||||
|
[uv + 1 for uv in polygon.loop_indices]
|
||||||
|
)
|
||||||
|
|
||||||
coordinates = self.file.createIfcCartesianPointList3D(
|
coordinates = self.file.createIfcCartesianPointList3D(
|
||||||
[self.convert_si_to_unit(v.co) for v in self.settings["geometry"].vertices]
|
[self.convert_si_to_unit(v.co) for v in self.settings["geometry"].vertices]
|
||||||
)
|
)
|
||||||
items = [self.file.createIfcTriangulatedFaceSet(coordinates, None, None, i) for i in ifc_raw_items if i]
|
|
||||||
|
if self.settings["should_generate_uvs"]:
|
||||||
|
# Blender supports multiple UV layers. We don't. Too bad.
|
||||||
|
tex_coords = self.file.createIfcTextureVertexList(
|
||||||
|
[tuple(x.uv) for x in self.settings["geometry"].uv_layers[0].data]
|
||||||
|
)
|
||||||
|
items = []
|
||||||
|
for i, coord_index in enumerate(ifc_raw_items):
|
||||||
|
if not coord_index:
|
||||||
|
continue
|
||||||
|
tex_coords_index = ifc_raw_uv_items[i]
|
||||||
|
face_set = self.file.createIfcTriangulatedFaceSet(coordinates, None, None, coord_index)
|
||||||
|
texture_map = self.file.createIfcIndexedTriangleTextureMap(
|
||||||
|
MappedTo=face_set, TexCoords=tex_coords, TexCoordIndex=tex_coords_index
|
||||||
|
)
|
||||||
|
items.append(face_set)
|
||||||
|
else:
|
||||||
|
items = [self.file.createIfcTriangulatedFaceSet(coordinates, None, None, i) for i in ifc_raw_items if i]
|
||||||
|
|
||||||
return self.file.createIfcShapeRepresentation(
|
return self.file.createIfcShapeRepresentation(
|
||||||
self.settings["context"],
|
self.settings["context"],
|
||||||
self.settings["context"].ContextIdentifier,
|
self.settings["context"].ContextIdentifier,
|
||||||
|
|||||||
Reference in New Issue
Block a user