WIP: MaterialX addon #104594
@ -11,7 +11,7 @@ from . import (
|
|||||||
register_classes, unregister_classes = bpy.utils.register_classes_factory([
|
register_classes, unregister_classes = bpy.utils.register_classes_factory([
|
||||||
ui.MATERIAL_OP_new_mx_node_tree,
|
ui.MATERIAL_OP_new_mx_node_tree,
|
||||||
ui.MATERIAL_OP_duplicate_mx_node_tree,
|
ui.MATERIAL_OP_duplicate_mx_node_tree,
|
||||||
ui.MATERIAL_OP_convert_shader_to_mx,
|
ui.MATERIAL_OP_convert_to_materialx,
|
||||||
ui.MATERIAL_OP_duplicate_mat_mx_node_tree,
|
ui.MATERIAL_OP_duplicate_mat_mx_node_tree,
|
||||||
ui.MATERIAL_OP_link_mx_node_tree,
|
ui.MATERIAL_OP_link_mx_node_tree,
|
||||||
ui.MATERIAL_OP_unlink_mx_node_tree,
|
ui.MATERIAL_OP_unlink_mx_node_tree,
|
||||||
@ -24,8 +24,8 @@ register_classes, unregister_classes = bpy.utils.register_classes_factory([
|
|||||||
ui.MATERIAL_OP_invoke_popup_shader_nodes,
|
ui.MATERIAL_OP_invoke_popup_shader_nodes,
|
||||||
ui.MATERIAL_OP_remove_node,
|
ui.MATERIAL_OP_remove_node,
|
||||||
ui.MATERIAL_OP_disconnect_node,
|
ui.MATERIAL_OP_disconnect_node,
|
||||||
ui.MATERIAL_OP_export_mx_file,
|
ui.MATERIAL_OP_export_file,
|
||||||
ui.MATERIAL_OP_export_mx_console,
|
ui.MATERIAL_OP_export_console,
|
||||||
ui.MATERIAL_PT_tools,
|
ui.MATERIAL_PT_tools,
|
||||||
ui.MATERIAL_PT_dev,
|
ui.MATERIAL_PT_dev,
|
||||||
])
|
])
|
||||||
|
@ -8,9 +8,9 @@ import MaterialX as mx
|
|||||||
|
|
||||||
from ..node_tree import MxNodeTree
|
from ..node_tree import MxNodeTree
|
||||||
from ..bl_nodes.output import ShaderNodeOutputMaterial
|
from ..bl_nodes.output import ShaderNodeOutputMaterial
|
||||||
from ..utils import MX_LIBS_DIR
|
from ..utils import MX_LIBS_DIR, mx_properties, get_temp_file, MaterialXProperties, with_prefix
|
||||||
|
|
||||||
from ..utils import logging, get_temp_file, MaterialXProperties
|
from .. import logging
|
||||||
log = logging.Log('material.properties')
|
log = logging.Log('material.properties')
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +18,29 @@ class MaterialProperties(MaterialXProperties):
|
|||||||
bl_type = bpy.types.Material
|
bl_type = bpy.types.Material
|
||||||
|
|
||||||
def update_mx_node_tree(self, context):
|
def update_mx_node_tree(self, context):
|
||||||
self.update()
|
# trying to show MaterialX area with node tree or Shader area
|
||||||
|
|
||||||
|
material = self.id_data
|
||||||
|
mx_node_tree = mx_properties(material).mx_node_tree
|
||||||
|
|
||||||
|
if not mx_node_tree:
|
||||||
|
return
|
||||||
|
|
||||||
|
screen = context.screen
|
||||||
|
if not hasattr(screen, 'areas'):
|
||||||
|
return
|
||||||
|
|
||||||
|
for window in context.window_manager.windows:
|
||||||
|
for area in window.screen.areas:
|
||||||
|
if area.ui_type not in (MxNodeTree.bl_idname, 'ShaderNodeTree'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
space = next(s for s in area.spaces if s.type == 'NODE_EDITOR')
|
||||||
|
if space.pin or space.shader_type != 'OBJECT':
|
||||||
|
continue
|
||||||
|
|
||||||
|
area.ui_type = MxNodeTree.bl_idname
|
||||||
|
space.node_tree = mx_node_tree
|
||||||
|
|
||||||
mx_node_tree: bpy.props.PointerProperty(type=MxNodeTree, update=update_mx_node_tree)
|
mx_node_tree: bpy.props.PointerProperty(type=MxNodeTree, update=update_mx_node_tree)
|
||||||
|
|
||||||
@ -33,8 +55,8 @@ class MaterialProperties(MaterialXProperties):
|
|||||||
node.bl_idname == ShaderNodeOutputMaterial.__name__ and
|
node.bl_idname == ShaderNodeOutputMaterial.__name__ and
|
||||||
node.is_active_output), None)
|
node.is_active_output), None)
|
||||||
|
|
||||||
def export(self, obj: bpy.types.Object) -> [mx.Document, None]:
|
def export(self, obj: bpy.types.Object, check_mx_node_tree=True) -> [mx.Document, None]:
|
||||||
if self.mx_node_tree:
|
if check_mx_node_tree and self.mx_node_tree:
|
||||||
return self.mx_node_tree.export()
|
return self.mx_node_tree.export()
|
||||||
|
|
||||||
material = self.id_data
|
material = self.id_data
|
||||||
@ -51,19 +73,7 @@ class MaterialProperties(MaterialXProperties):
|
|||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
def update(self, is_depsgraph=False):
|
def convert_to_materialx(self, obj: bpy.types.Object = None):
|
||||||
"""
|
|
||||||
Main update callback function, which notifies that material was updated from both:
|
|
||||||
depsgraph or MaterialX node tree
|
|
||||||
"""
|
|
||||||
if is_depsgraph and self.mx_node_tree:
|
|
||||||
return
|
|
||||||
|
|
||||||
material = self.id_data
|
|
||||||
# usd_node_tree.material_update(material)
|
|
||||||
# ViewportEngineScene.material_update(material)
|
|
||||||
|
|
||||||
def convert_shader_to_mx(self, obj: bpy.types.Object = None):
|
|
||||||
mat = self.id_data
|
mat = self.id_data
|
||||||
output_node = self.output_node
|
output_node = self.output_node
|
||||||
if not output_node:
|
if not output_node:
|
||||||
@ -95,6 +105,7 @@ class MaterialProperties(MaterialXProperties):
|
|||||||
mx.readFromXmlFile(doc, str(mtlx_file), searchPath=search_path)
|
mx.readFromXmlFile(doc, str(mtlx_file), searchPath=search_path)
|
||||||
mx_node_tree.import_(doc, mtlx_file)
|
mx_node_tree.import_(doc, mtlx_file)
|
||||||
self.mx_node_tree = mx_node_tree
|
self.mx_node_tree = mx_node_tree
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(traceback.format_exc(), mtlx_file)
|
log.error(traceback.format_exc(), mtlx_file)
|
||||||
return False
|
return False
|
||||||
@ -102,16 +113,6 @@ class MaterialProperties(MaterialXProperties):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def depsgraph_update(depsgraph):
|
|
||||||
if not depsgraph.updates:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Undo operation sends modified object with other stuff (scene, collection, etc...)
|
|
||||||
mat = next((upd.id for upd in depsgraph.updates if isinstance(upd.id, bpy.types.Material)), None)
|
|
||||||
if mat:
|
|
||||||
mat.hdusd.update(True)
|
|
||||||
|
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory((
|
register, unregister = bpy.utils.register_classes_factory((
|
||||||
MaterialProperties,
|
MaterialProperties,
|
||||||
))
|
))
|
||||||
|
@ -58,13 +58,13 @@ class MATERIAL_OP_duplicate_mx_node_tree(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class MATERIAL_OP_convert_shader_to_mx(bpy.types.Operator):
|
class MATERIAL_OP_convert_to_materialx(bpy.types.Operator):
|
||||||
"""Converts standard shader node tree to MaterialX node tree for selected material"""
|
"""Converts standard shader node tree to MaterialX node tree for selected material"""
|
||||||
bl_idname = utils.with_prefix('material_convert_shader_to_mx')
|
bl_idname = utils.with_prefix('material_convert_to_materialx')
|
||||||
bl_label = "Convert to MaterialX"
|
bl_label = "Convert to MaterialX"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if not mx_properties(context.material).convert_shader_to_mx(context.object):
|
if not mx_properties(context.material).convert_to_materialx(context.object):
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@ -131,12 +131,12 @@ class MATERIAL_PT_materialx(bpy.types.Panel):
|
|||||||
|
|
||||||
if mat_materialx.mx_node_tree:
|
if mat_materialx.mx_node_tree:
|
||||||
row.prop(mat_materialx.mx_node_tree, 'name', text="")
|
row.prop(mat_materialx.mx_node_tree, 'name', text="")
|
||||||
row.operator(MATERIAL_OP_convert_shader_to_mx.bl_idname, icon='FILE_TICK', text="")
|
row.operator(MATERIAL_OP_convert_to_materialx.bl_idname, icon='FILE_TICK', text="")
|
||||||
row.operator(MATERIAL_OP_duplicate_mx_node_tree.bl_idname, icon='DUPLICATE')
|
row.operator(MATERIAL_OP_duplicate_mx_node_tree.bl_idname, icon='DUPLICATE')
|
||||||
row.operator(MATERIAL_OP_unlink_mx_node_tree.bl_idname, icon='X')
|
row.operator(MATERIAL_OP_unlink_mx_node_tree.bl_idname, icon='X')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
row.operator(MATERIAL_OP_convert_shader_to_mx.bl_idname, icon='FILE_TICK', text="Convert")
|
row.operator(MATERIAL_OP_convert_to_materialx.bl_idname, icon='FILE_TICK', text="Convert")
|
||||||
row.operator(MATERIAL_OP_new_mx_node_tree.bl_idname, icon='ADD', text="")
|
row.operator(MATERIAL_OP_new_mx_node_tree.bl_idname, icon='ADD', text="")
|
||||||
|
|
||||||
|
|
||||||
@ -404,15 +404,15 @@ class MATERIAL_PT_materialx_surfaceshader(MATERIAL_PT_materialx_output):
|
|||||||
|
|
||||||
|
|
||||||
class MATERIAL_PT_materialx_displacementshader(MATERIAL_PT_materialx_output):
|
class MATERIAL_PT_materialx_displacementshader(MATERIAL_PT_materialx_output):
|
||||||
bl_idname = utils.with_prefix('MATERIAL_PT_materialx_sdisplacementshader', '_', True)
|
bl_idname = utils.with_prefix('MATERIAL_PT_materialx_displacementshader', '_', True)
|
||||||
bl_label = "Displacement Shader"
|
bl_label = "Displacement Shader"
|
||||||
bl_options = {'DEFAULT_CLOSED'}
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
|
||||||
out_key = 'displacementshader'
|
out_key = 'displacementshader'
|
||||||
|
|
||||||
|
|
||||||
class MATERIAL_OP_export_mx_file(bpy.types.Operator, ExportHelper):
|
class MATERIAL_OP_export_file(bpy.types.Operator, ExportHelper):
|
||||||
bl_idname = utils.with_prefix('material_export_mx_file')
|
bl_idname = utils.with_prefix('material_export_file')
|
||||||
bl_label = "Export MaterialX"
|
bl_label = "Export MaterialX"
|
||||||
bl_description = "Export material as MaterialX node tree to .mtlx file"
|
bl_description = "Export material as MaterialX node tree to .mtlx file"
|
||||||
|
|
||||||
@ -465,7 +465,7 @@ class MATERIAL_OP_export_mx_file(bpy.types.Operator, ExportHelper):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
materialx_prop = mx_properties(context.material)
|
materialx_prop = mx_properties(context.material)
|
||||||
|
|
||||||
if not materialx_prop.convert_shader_to_mx():
|
if not materialx_prop.convert_to_materialx():
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
doc = mx_properties(context.material).export(None)
|
doc = mx_properties(context.material).export(None)
|
||||||
@ -476,7 +476,7 @@ class MATERIAL_OP_export_mx_file(bpy.types.Operator, ExportHelper):
|
|||||||
self.filepath = str(Path(self.filepath).parent / context.material.name_full / Path(self.filepath).name)
|
self.filepath = str(Path(self.filepath).parent / context.material.name_full / Path(self.filepath).name)
|
||||||
|
|
||||||
utils.export_mx_to_file(doc, self.filepath,
|
utils.export_mx_to_file(doc, self.filepath,
|
||||||
mx_node_tree=materialx_prop.mx_node_tree,
|
mx_node_tree=None,
|
||||||
is_export_deps=self.is_export_deps,
|
is_export_deps=self.is_export_deps,
|
||||||
is_export_textures=self.is_export_textures,
|
is_export_textures=self.is_export_textures,
|
||||||
texture_dir_name=self.texture_dir_name,
|
texture_dir_name=self.texture_dir_name,
|
||||||
@ -498,13 +498,13 @@ class MATERIAL_OP_export_mx_file(bpy.types.Operator, ExportHelper):
|
|||||||
row.prop(self, 'texture_dir_name', text='')
|
row.prop(self, 'texture_dir_name', text='')
|
||||||
|
|
||||||
|
|
||||||
class MATERIAL_OP_export_mx_console(bpy.types.Operator):
|
class MATERIAL_OP_export_console(bpy.types.Operator):
|
||||||
bl_idname = utils.with_prefix('material_export_mx_console')
|
bl_idname = utils.with_prefix('material_export_console')
|
||||||
bl_label = "Export MaterialX to Console"
|
bl_label = "Export to Console"
|
||||||
bl_description = "Export material as MaterialX node tree to console"
|
bl_description = "Export material as MaterialX node tree to console"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
doc = mx_properties(context.material).export(context.object)
|
doc = mx_properties(context.material).export(context.object, False)
|
||||||
if not doc:
|
if not doc:
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
@ -526,10 +526,23 @@ class MATERIAL_PT_tools(bpy.types.Panel):
|
|||||||
return tree and tree.bl_idname == bpy.types.ShaderNodeTree.__name__
|
return tree and tree.bl_idname == bpy.types.ShaderNodeTree.__name__
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
mat_materialx = mx_properties(context.material)
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
layout.operator(MATERIAL_OP_convert_shader_to_mx.bl_idname, icon='FILE_TICK')
|
row = layout.row(align=True)
|
||||||
layout.operator(MATERIAL_OP_export_mx_file.bl_idname, text="Export MaterialX to file", icon='EXPORT')
|
row.menu(MATERIAL_MT_mx_node_tree.bl_idname, text="", icon='MATERIAL')
|
||||||
|
|
||||||
|
if mat_materialx.mx_node_tree:
|
||||||
|
row.prop(mat_materialx.mx_node_tree, 'name', text="")
|
||||||
|
row.operator(MATERIAL_OP_convert_to_materialx.bl_idname, icon='FILE_TICK', text="")
|
||||||
|
row.operator(MATERIAL_OP_duplicate_mx_node_tree.bl_idname, icon='DUPLICATE')
|
||||||
|
row.operator(MATERIAL_OP_unlink_mx_node_tree.bl_idname, icon='X')
|
||||||
|
|
||||||
|
else:
|
||||||
|
row.operator(MATERIAL_OP_convert_to_materialx.bl_idname, icon='FILE_TICK', text="Convert")
|
||||||
|
row.operator(MATERIAL_OP_new_mx_node_tree.bl_idname, icon='ADD', text="")
|
||||||
|
|
||||||
|
layout.operator(MATERIAL_OP_export_file.bl_idname, text="Export MaterialX to file", icon='EXPORT')
|
||||||
|
|
||||||
|
|
||||||
class MATERIAL_PT_dev(bpy.types.Panel):
|
class MATERIAL_PT_dev(bpy.types.Panel):
|
||||||
@ -546,4 +559,4 @@ class MATERIAL_PT_dev(bpy.types.Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator(MATERIAL_OP_export_mx_console.bl_idname)
|
layout.operator(MATERIAL_OP_export_console.bl_idname)
|
||||||
|
@ -14,8 +14,6 @@ log = logging.Log('node_tree')
|
|||||||
NODE_LAYER_SEPARATION_WIDTH = 280
|
NODE_LAYER_SEPARATION_WIDTH = 280
|
||||||
NODE_LAYER_SHIFT_X = 30
|
NODE_LAYER_SHIFT_X = 30
|
||||||
NODE_LAYER_SHIFT_Y = 100
|
NODE_LAYER_SHIFT_Y = 100
|
||||||
AREA_TO_UPDATE = 'PROPERTIES'
|
|
||||||
REGION_TO_UPDATE = 'WINDOW'
|
|
||||||
|
|
||||||
|
|
||||||
class MxNodeTree(bpy.types.ShaderNodeTree):
|
class MxNodeTree(bpy.types.ShaderNodeTree):
|
||||||
@ -251,17 +249,7 @@ class MxNodeTree(bpy.types.ShaderNodeTree):
|
|||||||
self.update_()
|
self.update_()
|
||||||
|
|
||||||
def update_(self):
|
def update_(self):
|
||||||
for material in bpy.data.materials:
|
utils.update_ui()
|
||||||
if utils.mx_properties(material).mx_node_tree and \
|
|
||||||
utils.mx_properties(material).mx_node_tree.name == self.name:
|
|
||||||
utils.mx_properties(material).update()
|
|
||||||
|
|
||||||
for window in bpy.context.window_manager.windows:
|
|
||||||
for area in window.screen.areas:
|
|
||||||
if area.type == AREA_TO_UPDATE:
|
|
||||||
for region in area.regions:
|
|
||||||
if region.type == REGION_TO_UPDATE:
|
|
||||||
region.tag_redraw()
|
|
||||||
|
|
||||||
# We have to call self.update_links via bpy.app.timers.register
|
# We have to call self.update_links via bpy.app.timers.register
|
||||||
# to have slight delay after self.update(). It'll be called once
|
# to have slight delay after self.update(). It'll be called once
|
||||||
|
@ -50,7 +50,7 @@ class NODES_OP_import_file(bpy.types.Operator, ImportHelper):
|
|||||||
|
|
||||||
class NODES_OP_export_file(bpy.types.Operator, ExportHelper):
|
class NODES_OP_export_file(bpy.types.Operator, ExportHelper):
|
||||||
bl_idname = utils.with_prefix('nodes_export_file')
|
bl_idname = utils.with_prefix('nodes_export_file')
|
||||||
bl_label = "Export MaterialX"
|
bl_label = "Export to File"
|
||||||
bl_description = "Export MaterialX node tree to .mtlx file"
|
bl_description = "Export MaterialX node tree to .mtlx file"
|
||||||
|
|
||||||
# region properties
|
# region properties
|
||||||
@ -137,7 +137,7 @@ class NODES_OP_export_file(bpy.types.Operator, ExportHelper):
|
|||||||
|
|
||||||
class NODES_OP_export_console(bpy.types.Operator):
|
class NODES_OP_export_console(bpy.types.Operator):
|
||||||
bl_idname = utils.with_prefix('nodes_export_console')
|
bl_idname = utils.with_prefix('nodes_export_console')
|
||||||
bl_label = "Export MaterialX to Console"
|
bl_label = "Export to Console"
|
||||||
bl_description = "Export MaterialX node tree to console"
|
bl_description = "Export MaterialX node tree to console"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@ -183,7 +183,7 @@ class NODES_PT_tools(bpy.types.Panel):
|
|||||||
|
|
||||||
layout.operator(NODES_OP_create_basic_nodes.bl_idname, icon='ADD')
|
layout.operator(NODES_OP_create_basic_nodes.bl_idname, icon='ADD')
|
||||||
layout.operator(NODES_OP_import_file.bl_idname, icon='IMPORT')
|
layout.operator(NODES_OP_import_file.bl_idname, icon='IMPORT')
|
||||||
layout.operator(NODES_OP_export_file.bl_idname, icon='EXPORT', text='Export MaterialX to file')
|
layout.operator(NODES_OP_export_file.bl_idname, icon='EXPORT')
|
||||||
|
|
||||||
|
|
||||||
class NODES_PT_dev(bpy.types.Panel):
|
class NODES_PT_dev(bpy.types.Panel):
|
||||||
|
@ -30,12 +30,6 @@ MATLIB_URL = "https://api.matlib.gpuopen.com/api"
|
|||||||
|
|
||||||
TEMP_FOLDER = "bl-materialx"
|
TEMP_FOLDER = "bl-materialx"
|
||||||
|
|
||||||
SUPPORTED_FORMATS = {".png", ".jpeg", ".jpg", ".hdr", ".tga", ".bmp"}
|
|
||||||
DEFAULT_FORMAT = ".hdr"
|
|
||||||
BLENDER_DEFAULT_FORMAT = "HDR"
|
|
||||||
BLENDER_DEFAULT_COLOR_MODE = "RGB"
|
|
||||||
READONLY_IMAGE_FORMATS = {".dds"} # blender can read these formats, but can't write
|
|
||||||
|
|
||||||
os.environ['MATERIALX_SEARCH_PATH'] = str(MX_LIBS_DIR)
|
os.environ['MATERIALX_SEARCH_PATH'] = str(MX_LIBS_DIR)
|
||||||
|
|
||||||
|
|
||||||
@ -323,13 +317,13 @@ def export_mx_to_file(doc, filepath, *, mx_node_tree=None, is_export_deps=False,
|
|||||||
dest_path = texture_dir / f"{source_path.stem}{source_path.suffix}"
|
dest_path = texture_dir / f"{source_path.stem}{source_path.suffix}"
|
||||||
|
|
||||||
shutil.copy(source_path, dest_path)
|
shutil.copy(source_path, dest_path)
|
||||||
log(f"Export file {source_path} to {dest_path}: completed successfuly")
|
log(f"Export file {source_path} to {dest_path}: completed successfully")
|
||||||
|
|
||||||
rel_dest_path = dest_path.relative_to(root_dir)
|
rel_dest_path = dest_path.relative_to(root_dir)
|
||||||
mx_input.setValue(str(rel_dest_path), mx_input.getType())
|
mx_input.setValue(str(rel_dest_path), mx_input.getType())
|
||||||
|
|
||||||
mx.writeToXmlFile(doc, filepath)
|
mx.writeToXmlFile(doc, filepath)
|
||||||
log(f"Export MaterialX to {filepath}: completed successfuly")
|
log(f"Export MaterialX to {filepath}: completed successfully")
|
||||||
|
|
||||||
|
|
||||||
def temp_dir():
|
def temp_dir():
|
||||||
@ -354,13 +348,14 @@ def get_temp_file(suffix, name=None, is_rand=False):
|
|||||||
return temp_dir() / name
|
return temp_dir() / name
|
||||||
|
|
||||||
|
|
||||||
def cache_image_file(image: bpy.types.Image, cache_check=True):
|
SUPPORTED_FORMATS = {".png", ".jpeg", ".jpg", ".hdr", ".tga", ".bmp"}
|
||||||
SUPPORTED_FORMATS = {".png", ".jpeg", ".jpg", ".hdr", ".tga", ".bmp"}
|
DEFAULT_FORMAT = ".hdr"
|
||||||
DEFAULT_FORMAT = ".hdr"
|
BLENDER_DEFAULT_FORMAT = "HDR"
|
||||||
BLENDER_DEFAULT_FORMAT = "HDR"
|
BLENDER_DEFAULT_COLOR_MODE = "RGB"
|
||||||
BLENDER_DEFAULT_COLOR_MODE = "RGB"
|
READONLY_IMAGE_FORMATS = {".dds"} # blender can read these formats, but can't write
|
||||||
READONLY_IMAGE_FORMATS = {".dds"} # blender can read these formats, but can't write
|
|
||||||
|
|
||||||
|
|
||||||
|
def cache_image_file(image: bpy.types.Image, cache_check=True):
|
||||||
image_path = Path(image.filepath_from_user())
|
image_path = Path(image.filepath_from_user())
|
||||||
if not image.packed_file and image.source != 'GENERATED':
|
if not image.packed_file and image.source != 'GENERATED':
|
||||||
if not image_path.is_file():
|
if not image_path.is_file():
|
||||||
@ -401,6 +396,23 @@ def cache_image_file(image: bpy.types.Image, cache_check=True):
|
|||||||
return temp_path
|
return temp_path
|
||||||
|
|
||||||
|
|
||||||
|
def cache_image_file_path(image_path, cache_check=True):
|
||||||
|
if image_path.suffix.lower() in SUPPORTED_FORMATS:
|
||||||
|
return image_path
|
||||||
|
|
||||||
|
if cache_check:
|
||||||
|
temp_path = get_temp_file(DEFAULT_FORMAT, image_path.name)
|
||||||
|
if temp_path.is_file():
|
||||||
|
return temp_path
|
||||||
|
|
||||||
|
image = bpy.data.images.load(str(image_path))
|
||||||
|
try:
|
||||||
|
return cache_image_file(image, cache_check)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
bpy.data.images.remove(image)
|
||||||
|
|
||||||
|
|
||||||
def pass_node_reroute(link):
|
def pass_node_reroute(link):
|
||||||
while isinstance(link.from_node, bpy.types.NodeReroute):
|
while isinstance(link.from_node, bpy.types.NodeReroute):
|
||||||
if not link.from_node.inputs[0].links:
|
if not link.from_node.inputs[0].links:
|
||||||
@ -418,61 +430,3 @@ def update_ui(area_type='PROPERTIES', region_type='WINDOW'):
|
|||||||
for region in area.regions:
|
for region in area.regions:
|
||||||
if region.type == region_type:
|
if region.type == region_type:
|
||||||
region.tag_redraw()
|
region.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
def cache_image_file(image: bpy.types.Image, cache_check=True):
|
|
||||||
image_path = Path(image.filepath_from_user())
|
|
||||||
if not image.packed_file and image.source != 'GENERATED':
|
|
||||||
if not image_path.is_file():
|
|
||||||
log.warn("Image is missing", image, image_path)
|
|
||||||
return None
|
|
||||||
|
|
||||||
image_suffix = image_path.suffix.lower()
|
|
||||||
|
|
||||||
if image_suffix in SUPPORTED_FORMATS and\
|
|
||||||
f".{image.file_format.lower()}" in SUPPORTED_FORMATS and not image.is_dirty:
|
|
||||||
return image_path
|
|
||||||
|
|
||||||
if image_suffix in READONLY_IMAGE_FORMATS:
|
|
||||||
return image_path
|
|
||||||
|
|
||||||
temp_path = get_temp_file(DEFAULT_FORMAT, image_path.stem)
|
|
||||||
if cache_check and image.source != 'GENERATED' and temp_path.is_file():
|
|
||||||
return temp_path
|
|
||||||
|
|
||||||
scene = bpy.context.scene
|
|
||||||
user_format = scene.render.image_settings.file_format
|
|
||||||
user_color_mode = scene.render.image_settings.color_mode
|
|
||||||
|
|
||||||
# in some scenes the color_mode is undefined
|
|
||||||
# we can read it but unable to assign back, so switch it to 'RGB' if color_mode isn't selected
|
|
||||||
if not user_color_mode:
|
|
||||||
user_color_mode = 'RGB'
|
|
||||||
|
|
||||||
scene.render.image_settings.file_format = BLENDER_DEFAULT_FORMAT
|
|
||||||
scene.render.image_settings.color_mode = BLENDER_DEFAULT_COLOR_MODE
|
|
||||||
|
|
||||||
try:
|
|
||||||
image.save_render(filepath=str(temp_path))
|
|
||||||
finally:
|
|
||||||
scene.render.image_settings.file_format = user_format
|
|
||||||
scene.render.image_settings.color_mode = user_color_mode
|
|
||||||
|
|
||||||
return temp_path
|
|
||||||
|
|
||||||
|
|
||||||
def cache_image_file_path(image_path, cache_check=True):
|
|
||||||
if image_path.suffix.lower() in SUPPORTED_FORMATS:
|
|
||||||
return image_path
|
|
||||||
|
|
||||||
if cache_check:
|
|
||||||
temp_path = get_temp_file(DEFAULT_FORMAT, image_path.name)
|
|
||||||
if temp_path.is_file():
|
|
||||||
return temp_path
|
|
||||||
|
|
||||||
image = bpy.data.images.load(str(image_path))
|
|
||||||
try:
|
|
||||||
return cache_image_file(image, cache_check)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
bpy.data.images.remove(image)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user