mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 05:14:30 +00:00
Support loading styles with IfcPixelTexture
This commit is contained in:
@@ -241,10 +241,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
mode = texture.get("Mode", None)
|
mode = texture.get("Mode", None)
|
||||||
node = None
|
node = None
|
||||||
|
|
||||||
if texture["type"] == "IfcPixelTexture":
|
|
||||||
print(f"WARNING. Texture of type {texture['type']} is not currently supported, it will be skipped.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
image_url = None
|
image_url = None
|
||||||
|
|
||||||
def get_image():
|
def get_image():
|
||||||
@@ -284,9 +280,35 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
).ravel()
|
).ravel()
|
||||||
return bpy_image
|
return bpy_image
|
||||||
|
|
||||||
# TODO: IfcPixelTexture
|
# IfcPixelTexture
|
||||||
print(f"WARNING. Texture of type {texture['type']} is not yet supported.")
|
n_components = texture["ColourComponents"]
|
||||||
return
|
width, height = texture["Width"], texture["Height"]
|
||||||
|
blender_pixel_data = np.ones(width * height * 4, dtype=np.float32)
|
||||||
|
|
||||||
|
# according to https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPixelTexture.htm
|
||||||
|
# 1 component - grey scale intensity value
|
||||||
|
# 2 components - grey scale + alpha
|
||||||
|
# 3 components - RGB
|
||||||
|
# 4 components - RGBA
|
||||||
|
for i, pixel_str in enumerate(iterable=texture["Pixel"]):
|
||||||
|
pixel_bytes = int(pixel_str, 2).to_bytes(len(pixel_str) // 8, "big")
|
||||||
|
pixel_values = np.array(list(pixel_bytes)) / 255
|
||||||
|
cur_pos = i * 4
|
||||||
|
|
||||||
|
if n_components in (1, 2):
|
||||||
|
blender_pixel_data[cur_pos : cur_pos + 3] = pixel_values[0]
|
||||||
|
if n_components == 2:
|
||||||
|
blender_pixel_data[cur_pos + 3] = pixel_values[1]
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 3, 4 components
|
||||||
|
blender_pixel_data[cur_pos : cur_pos + 3] = pixel_values[:3]
|
||||||
|
if n_components == 4:
|
||||||
|
blender_pixel_data[cur_pos + 3] = pixel_values[3]
|
||||||
|
|
||||||
|
bpy_image = bpy.data.images.new("pixel_texture", width=width, height=height)
|
||||||
|
bpy_image.pixels[:] = blender_pixel_data
|
||||||
|
return bpy_image
|
||||||
|
|
||||||
if reflectance_method in ["PHYSICAL", "NOTDEFINED"]:
|
if reflectance_method in ["PHYSICAL", "NOTDEFINED"]:
|
||||||
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
|
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
|
||||||
|
|||||||
@@ -254,6 +254,120 @@ class TestCreatingStyles(NewFile):
|
|||||||
assert image_node.image.pixels[:] == (0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
|
assert image_node.image.pixels[:] == (0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
def test_create_surface_style_with_textures_from_ifc_pixel_image(self):
|
||||||
|
# fmt: off
|
||||||
|
pixel_data_for_components = {
|
||||||
|
1: (
|
||||||
|
0.5, 0.5, 0.5, 1.0,
|
||||||
|
0.6, 0.6, 0.6, 1.0,
|
||||||
|
0.7, 0.7, 0.7, 1.0,
|
||||||
|
1.0, 1.0, 1.0, 1.0,
|
||||||
|
),
|
||||||
|
2: (
|
||||||
|
0.5, 0.5, 0.5, 0.5,
|
||||||
|
0.6, 0.6, 0.6, 0.5,
|
||||||
|
0.7, 0.7, 0.7, 0.5,
|
||||||
|
1.0, 1.0, 1.0, 0.5,
|
||||||
|
),
|
||||||
|
3: (
|
||||||
|
0.5, 0, 0, 1.0,
|
||||||
|
0, 0.6, 0, 1.0,
|
||||||
|
0, 0, 0.7, 1.0,
|
||||||
|
1, 1, 1, 1.0,
|
||||||
|
),
|
||||||
|
4: (
|
||||||
|
0.5, 0, 0, 0.5,
|
||||||
|
0, 0.6, 0, 0.5,
|
||||||
|
0, 0, 0.7, 0.5,
|
||||||
|
1, 1, 1, 0.5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
# fmt: on
|
||||||
|
for i in range(1, 5):
|
||||||
|
self.run_test_create_surface_style_with_textures_from_ifc_pixel_image(i, pixel_data_for_components[i])
|
||||||
|
|
||||||
|
def run_test_create_surface_style_with_textures_from_ifc_pixel_image(self, n_components, pixel_data):
|
||||||
|
# this case occurs when we edit shader properties without saving them to IFC
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
style = tool.Ifc.run("style.add_style", name="test")
|
||||||
|
material = bpy.data.materials.new(style.Name)
|
||||||
|
tool.Ifc.link(style, material)
|
||||||
|
get_color = lambda value: {color: value for color in ("Red", "Green", "Blue")}
|
||||||
|
rendering_attributes = {
|
||||||
|
"ReflectanceMethod": "NOTDEFINED",
|
||||||
|
"DiffuseColour": get_color(0.5),
|
||||||
|
"SurfaceColour": get_color(0.3),
|
||||||
|
"Transparency": 0.3,
|
||||||
|
"SpecularHighlight": {"IfcSpecularRoughness": 0.4},
|
||||||
|
"SpecularColour": 0.03,
|
||||||
|
}
|
||||||
|
rendering_style = tool.Ifc.run(
|
||||||
|
"style.add_surface_style",
|
||||||
|
style=style,
|
||||||
|
ifc_class="IfcSurfaceStyleRendering",
|
||||||
|
attributes=rendering_attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_pixels_binary(n_components):
|
||||||
|
# just 4 pixels - red, green, blue, white
|
||||||
|
# fmt: off
|
||||||
|
pixel_data = (
|
||||||
|
0.5, 0, 0, 0.5,
|
||||||
|
0, 0.6, 0, 0.5,
|
||||||
|
0, 0, 0.7, 0.5,
|
||||||
|
1, 1, 1, 0.5,
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
pixels = np.array(pixel_data).reshape(-1, 4)
|
||||||
|
alpha = pixels[:, 3]
|
||||||
|
if n_components in (1, 2):
|
||||||
|
pixels = [[p[min(i, 2)]] for i, p in enumerate(pixels)]
|
||||||
|
if n_components == 2:
|
||||||
|
for i in range(4):
|
||||||
|
pixels[i].append(alpha[i])
|
||||||
|
elif n_components == 3:
|
||||||
|
pixels = pixels[:, :3]
|
||||||
|
|
||||||
|
pixels = np.array(pixels) * 255
|
||||||
|
return ["".join(["{:08b}".format(int(i)) for i in pixel]) for pixel in pixels]
|
||||||
|
|
||||||
|
texture_data = {
|
||||||
|
"Pixel": get_pixels_binary(n_components),
|
||||||
|
"ColourComponents": n_components,
|
||||||
|
"Width": 2,
|
||||||
|
"Height": 2,
|
||||||
|
"Mode": "DIFFUSE",
|
||||||
|
"RepeatS": True,
|
||||||
|
"RepeatT": True,
|
||||||
|
}
|
||||||
|
# setup texture manually as `style.add_surface_textures` doesn't support non-IfcImageTexture textures
|
||||||
|
textures = [ifc_file.create_entity("IfcPixelTexture", **texture_data)]
|
||||||
|
# setup UV
|
||||||
|
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD")
|
||||||
|
|
||||||
|
texture_style = tool.Ifc.run(
|
||||||
|
"style.add_surface_style",
|
||||||
|
style=style,
|
||||||
|
ifc_class="IfcSurfaceStyleWithTextures",
|
||||||
|
attributes={"Textures": textures},
|
||||||
|
)
|
||||||
|
|
||||||
|
subject.create_surface_style_rendering(material, rendering_style)
|
||||||
|
subject.create_surface_style_with_textures(material, rendering_style, texture_style)
|
||||||
|
|
||||||
|
used_node_types = set([n.type for n in material.node_tree.nodes[:]])
|
||||||
|
assert used_node_types == set((["OUTPUT_MATERIAL", "BSDF_PRINCIPLED", "TEX_IMAGE", "TEX_COORD"]))
|
||||||
|
|
||||||
|
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
|
||||||
|
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
|
||||||
|
assert image_node.inputs["Vector"].links[0].from_socket.name == "Generated"
|
||||||
|
assert image_node.image.filepath == ""
|
||||||
|
assert np.allclose(
|
||||||
|
image_node.image.pixels[:], pixel_data, atol=0.01
|
||||||
|
), f"Failed to match pixels for {n_components}.\nBlender pixel_data: {image_node.image.pixels[:]}.\nExpected data: {pixel_data}"
|
||||||
|
|
||||||
|
|
||||||
class TestLoadingIndexedTextureMap(NewFile):
|
class TestLoadingIndexedTextureMap(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user