| 295 | image_url = None |
| 296 | |
| 297 | def get_image() -> Union[bpy.types.Image, None]: |
| 298 | # TODO: orphaned textures after shader recreated? |
| 299 | if texture["type"] == "IfcImageTexture": |
| 300 | # IFC2X3 uses UrlReference, IFC4+ uses URLReference. |
| 301 | original_image_url = texture.get("URLReference") or texture.get("UrlReference", "") |
| 302 | is_relative = not os.path.isabs(original_image_url) |
| 303 | nonlocal image_url |
| 304 | image_url = Path(original_image_url) |
| 305 | if is_relative: |
| 306 | ifc_path = Path(tool.Ifc.get_path()) |
| 307 | image_url = ifc_path.parent / image_url |
| 308 | image_url = image_url.absolute().resolve() |
| 309 | |
| 310 | if not image_url.exists(): |
| 311 | print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.") |
| 312 | return |
| 313 | |
| 314 | # keep url relative if it was before |
| 315 | image_url = str(image_url) |
| 316 | if is_relative and bpy.data.filepath: |
| 317 | image_url = bpy.path.relpath(image_url) |
| 318 | return bpy.data.images.load(image_url) |
| 319 | |
| 320 | elif texture["type"] == "IfcBlobTexture": |
| 321 | # https://blender.stackexchange.com/questions/173206/how-to-efficiently-convert-a-pil-image-to-bpy-types-image |
| 322 | # https://blender.stackexchange.com/questions/62072/does-blender-have-a-method-to-a-get-png-formatted-bytearray-for-an-image-via-pyt |
| 323 | import io |
| 324 | |
| 325 | from PIL import Image |
| 326 | |
| 327 | value = texture["RasterCode"] |
| 328 | image_bytes = int(value, 2).to_bytes(len(value) // 8, "big") |
| 329 | pil_image = Image.open(io.BytesIO(image_bytes)) |
| 330 | byte_to_normalized = 1.0 / 255.0 |
| 331 | bpy_image = bpy.data.images.new("blob_texture", width=pil_image.width, height=pil_image.height) |
| 332 | # PIL returns rows ordered from top to bottom, blender from bottom to top |
| 333 | pil_pixel_data = np.asarray(pil_image.convert("RGBA"), dtype=np.float32) |
| 334 | bpy_image.pixels[:] = (pil_pixel_data * byte_to_normalized)[::-1].ravel() |
| 335 | bpy_image.pack() |
| 336 | return bpy_image |
| 337 | |
| 338 | # IfcPixelTexture |
| 339 | n_components = texture["ColourComponents"] |
| 340 | width, height = texture["Width"], texture["Height"] |
| 341 | blender_pixel_data = np.ones(width * height * 4, dtype=np.float32) |
| 342 | |
| 343 | # according to https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPixelTexture.htm |
| 344 | # 1 component - grey scale intensity value |
| 345 | # 2 components - grey scale + alpha |
| 346 | # 3 components - RGB |
| 347 | # 4 components - RGBA |
| 348 | for i, pixel_str in enumerate(iterable=texture["Pixel"]): |
| 349 | pixel_bytes = int(pixel_str, 2).to_bytes(len(pixel_str) // 8, "big") |
| 350 | pixel_values = np.array(list(pixel_bytes)) / 255 |
| 351 | cur_pos = i * 4 |
| 352 | |
| 353 | if n_components in (1, 2): |
| 354 | blender_pixel_data[cur_pos : cur_pos + 3] = pixel_values[0] |