mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 14:31:39 +00:00
BBIM to support loading IfcBlobTexture
This commit is contained in:
@@ -23,6 +23,7 @@ import ifcopenshell.util.element
|
|||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import os
|
import os
|
||||||
|
import numpy as np
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -240,25 +241,52 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
mode = texture.get("Mode", None)
|
mode = texture.get("Mode", None)
|
||||||
node = None
|
node = None
|
||||||
|
|
||||||
if texture["type"] != "IfcImageTexture":
|
if texture["type"] == "IfcPixelTexture":
|
||||||
print(f"WARNING. Texture of type {texture['type']} is not currently supported, it will be skipped.")
|
print(f"WARNING. Texture of type {texture['type']} is not currently supported, it will be skipped.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
original_image_url = texture["URLReference"]
|
image_url = None
|
||||||
is_relative = not os.path.isabs(original_image_url)
|
|
||||||
image_url = Path(original_image_url)
|
|
||||||
if is_relative:
|
|
||||||
ifc_path = Path(tool.Ifc.get_path())
|
|
||||||
image_url = ifc_path.parent / image_url
|
|
||||||
|
|
||||||
if not image_url.exists():
|
def get_image():
|
||||||
print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.")
|
# TODO: orphaned textures after shader recreated?
|
||||||
continue
|
if texture["type"] == "IfcImageTexture":
|
||||||
|
original_image_url = texture["URLReference"]
|
||||||
|
is_relative = not os.path.isabs(original_image_url)
|
||||||
|
nonlocal image_url
|
||||||
|
image_url = Path(original_image_url)
|
||||||
|
if is_relative:
|
||||||
|
ifc_path = Path(tool.Ifc.get_path())
|
||||||
|
image_url = ifc_path.parent / image_url
|
||||||
|
|
||||||
# keep url relative if it was before
|
if not image_url.exists():
|
||||||
image_url = str(image_url)
|
print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.")
|
||||||
if is_relative and bpy.data.filepath:
|
return
|
||||||
image_url = bpy.path.relpath(image_url)
|
|
||||||
|
# keep url relative if it was before
|
||||||
|
image_url = str(image_url)
|
||||||
|
if is_relative and bpy.data.filepath:
|
||||||
|
image_url = bpy.path.relpath(image_url)
|
||||||
|
return bpy.data.images.load(image_url)
|
||||||
|
|
||||||
|
elif texture["type"] == "IfcBlobTexture":
|
||||||
|
# https://blender.stackexchange.com/questions/173206/how-to-efficiently-convert-a-pil-image-to-bpy-types-image
|
||||||
|
# https://blender.stackexchange.com/questions/62072/does-blender-have-a-method-to-a-get-png-formatted-bytearray-for-an-image-via-pyt
|
||||||
|
import io
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
value = texture["RasterCode"]
|
||||||
|
image_bytes = int(value, 2).to_bytes(len(value) // 8, "big")
|
||||||
|
pil_image = Image.open(io.BytesIO(image_bytes))
|
||||||
|
byte_to_normalized = 1.0 / 255.0
|
||||||
|
bpy_image = bpy.data.images.new("blob_texture", width=pil_image.width, height=pil_image.height)
|
||||||
|
bpy_image.pixels[:] = (
|
||||||
|
np.asarray(pil_image.convert("RGBA"), dtype=np.float32) * byte_to_normalized
|
||||||
|
).ravel()
|
||||||
|
return bpy_image
|
||||||
|
|
||||||
|
# TODO: IfcPixelTexture
|
||||||
|
print(f"WARNING. Texture of type {texture['type']} is not yet supported.")
|
||||||
|
return
|
||||||
|
|
||||||
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")
|
||||||
@@ -267,9 +295,13 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
if mode not in SUPPORTED_PBR_TEXTURES:
|
if mode not in SUPPORTED_PBR_TEXTURES:
|
||||||
print(
|
print(
|
||||||
f"WARNING. Texture with {mode} Mode is not supported for style with PHYSICAL reflectance method.\n"
|
f"WARNING. Texture with {mode} Mode is not supported for style with PHYSICAL reflectance method.\n"
|
||||||
f"Supported types are: {', '.join(SUPPORTED_PBR_TEXTURES)}\n"
|
f"Supported types are: {', '.join(SUPPORTED_PBR_TEXTURES)}"
|
||||||
f"Texture by path {image_url} will be skipped."
|
|
||||||
)
|
)
|
||||||
|
if texture["type"] == "IfcImageTexture":
|
||||||
|
print(f"Texture by path {image_url} will be skipped.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (image := get_image()) is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mode == "NORMAL":
|
if mode == "NORMAL":
|
||||||
@@ -281,7 +313,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
# add normal map sampler
|
# add normal map sampler
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = normalmap.location - Vector((300, 0))
|
node.location = normalmap.location - Vector((300, 0))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
image.colorspace_settings.name = "Non-Color"
|
image.colorspace_settings.name = "Non-Color"
|
||||||
node.image = image
|
node.image = image
|
||||||
blender_material.node_tree.links.new(node.outputs[0], normalmap.inputs["Color"])
|
blender_material.node_tree.links.new(node.outputs[0], normalmap.inputs["Color"])
|
||||||
@@ -303,7 +334,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
# add emission texture sampler
|
# add emission texture sampler
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = emission.location - Vector((350, 0))
|
node.location = emission.location - Vector((350, 0))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
node.image = image
|
node.image = image
|
||||||
blender_material.node_tree.links.new(node.outputs[0], emission.inputs[0])
|
blender_material.node_tree.links.new(node.outputs[0], emission.inputs[0])
|
||||||
|
|
||||||
@@ -315,7 +345,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
|
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = separate.location - Vector((300, 0))
|
node.location = separate.location - Vector((300, 0))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
image.colorspace_settings.name = "Non-Color"
|
image.colorspace_settings.name = "Non-Color"
|
||||||
node.image = image
|
node.image = image
|
||||||
blender_material.node_tree.links.new(node.outputs[0], separate.inputs[0])
|
blender_material.node_tree.links.new(node.outputs[0], separate.inputs[0])
|
||||||
@@ -341,7 +370,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
|
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = group.location - Vector((300, 0))
|
node.location = group.location - Vector((300, 0))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
image.colorspace_settings.name = "Non-Color"
|
image.colorspace_settings.name = "Non-Color"
|
||||||
node.image = image
|
node.image = image
|
||||||
blender_material.node_tree.links.new(node.outputs[0], group.inputs["Occlusion"])
|
blender_material.node_tree.links.new(node.outputs[0], group.inputs["Occlusion"])
|
||||||
@@ -349,7 +377,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
elif mode == "DIFFUSE":
|
elif mode == "DIFFUSE":
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = bsdf.location - Vector((400, 0))
|
node.location = bsdf.location - Vector((400, 0))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
node.image = image
|
node.image = image
|
||||||
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs["Base Color"])
|
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs["Base Color"])
|
||||||
# leave it to default(OPAQUE) when no Transparency defined
|
# leave it to default(OPAQUE) when no Transparency defined
|
||||||
@@ -360,10 +387,14 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
elif reflectance_method == "FLAT":
|
elif reflectance_method == "FLAT":
|
||||||
bsdf = tool.Blender.get_material_node(blender_material, "MIX_SHADER")
|
bsdf = tool.Blender.get_material_node(blender_material, "MIX_SHADER")
|
||||||
if mode != "EMISSIVE":
|
if mode != "EMISSIVE":
|
||||||
print(
|
print("WARNING. Only EMISSIVE Mode textures are supported for style with FLAT reflectance method.")
|
||||||
"WARNING. Only EMISSIVE Mode textures are supported for style with FLAT reflectance method.\n"
|
if texture["type"] == "IfcImageTexture":
|
||||||
f"{mode} Mode texture by path {image_url} will be skipped."
|
print(f"{mode} Mode texture by path {image_url} will be skipped.")
|
||||||
)
|
else:
|
||||||
|
print(f"{mode} Mode texture will be skipped.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (image := get_image) is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# remove RGB node from `create_surface_style_rendering`
|
# remove RGB node from `create_surface_style_rendering`
|
||||||
@@ -372,8 +403,6 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
|
|
||||||
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
node.location = bsdf.location - Vector((200, 250))
|
node.location = bsdf.location - Vector((200, 250))
|
||||||
image = bpy.data.images.load(image_url)
|
|
||||||
# TODO: orphaned textures after shader recreated?
|
|
||||||
node.image = image
|
node.image = image
|
||||||
|
|
||||||
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs[2])
|
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs[2])
|
||||||
|
|||||||
Reference in New Issue
Block a user