feat: added initial nodegroup support
This commit is contained in:
@ -37,7 +37,8 @@ __all__ = [
|
|||||||
'bl_speaker',
|
'bl_speaker',
|
||||||
'bl_font',
|
'bl_font',
|
||||||
'bl_sound',
|
'bl_sound',
|
||||||
'bl_file'
|
'bl_file',
|
||||||
|
'bl_node_group'
|
||||||
] # Order here defines execution order
|
] # Order here defines execution order
|
||||||
|
|
||||||
from . import *
|
from . import *
|
||||||
|
@ -40,11 +40,15 @@ def load_node(node_data, node_tree):
|
|||||||
target_node.select = False
|
target_node.select = False
|
||||||
loader.load(target_node, node_data)
|
loader.load(target_node, node_data)
|
||||||
image_uuid = node_data.get('image_uuid', None)
|
image_uuid = node_data.get('image_uuid', None)
|
||||||
|
node_tree_uuid = node_data.get('node_tree_uuid', None)
|
||||||
|
|
||||||
if image_uuid and not target_node.image:
|
if image_uuid and not target_node.image:
|
||||||
target_node.image = get_datablock_from_uuid(image_uuid, None)
|
target_node.image = get_datablock_from_uuid(image_uuid, None)
|
||||||
|
|
||||||
for idx, inpt in enumerate(node_data["inputs"]):
|
if node_tree_uuid:
|
||||||
|
target_node.node_tree = get_datablock_from_uuid(node_tree_uuid, None)
|
||||||
|
|
||||||
|
for idx, inpt in enumerate(node_data['inputs']):
|
||||||
if hasattr(target_node.inputs[idx], "default_value"):
|
if hasattr(target_node.inputs[idx], "default_value"):
|
||||||
try:
|
try:
|
||||||
target_node.inputs[idx].default_value = inpt["default_value"]
|
target_node.inputs[idx].default_value = inpt["default_value"]
|
||||||
@ -52,7 +56,7 @@ def load_node(node_data, node_tree):
|
|||||||
logging.error(
|
logging.error(
|
||||||
f"Material {inpt.keys()} parameter not supported, skipping")
|
f"Material {inpt.keys()} parameter not supported, skipping")
|
||||||
|
|
||||||
for idx, output in enumerate(node_data["outputs"]):
|
for idx, output in enumerate(node_data['outputs']):
|
||||||
if hasattr(target_node.outputs[idx], "default_value"):
|
if hasattr(target_node.outputs[idx], "default_value"):
|
||||||
try:
|
try:
|
||||||
target_node.outputs[idx].default_value = output["default_value"]
|
target_node.outputs[idx].default_value = output["default_value"]
|
||||||
@ -148,6 +152,9 @@ def dump_node(node):
|
|||||||
io_dumper.depth = 2
|
io_dumper.depth = 2
|
||||||
io_dumper.include_filter = ["default_value"]
|
io_dumper.include_filter = ["default_value"]
|
||||||
|
|
||||||
|
if node.type in ['GROUP_INPUT', 'GROUP_OUTPUT']:
|
||||||
|
io_dumper.include_filter.extend(['name','type'])
|
||||||
|
|
||||||
for idx, inpt in enumerate(node.inputs):
|
for idx, inpt in enumerate(node.inputs):
|
||||||
if hasattr(inpt, 'default_value'):
|
if hasattr(inpt, 'default_value'):
|
||||||
dumped_node['inputs'].append(io_dumper.dump(inpt))
|
dumped_node['inputs'].append(io_dumper.dump(inpt))
|
||||||
@ -178,6 +185,8 @@ def dump_node(node):
|
|||||||
dumped_node['mapping'] = curve_dumper.dump(node.mapping)
|
dumped_node['mapping'] = curve_dumper.dump(node.mapping)
|
||||||
if hasattr(node, 'image') and getattr(node, 'image'):
|
if hasattr(node, 'image') and getattr(node, 'image'):
|
||||||
dumped_node['image_uuid'] = node.image.uuid
|
dumped_node['image_uuid'] = node.image.uuid
|
||||||
|
if hasattr(node, 'node_tree') and getattr(node, 'node_tree'):
|
||||||
|
dumped_node['node_tree_uuid'] = node.node_tree.uuid
|
||||||
return dumped_node
|
return dumped_node
|
||||||
|
|
||||||
|
|
||||||
@ -188,11 +197,19 @@ def dump_shader_node_tree(node_tree:bpy.types.ShaderNodeTree)->dict:
|
|||||||
:type node_tree: bpy.types.ShaderNodeTree
|
:type node_tree: bpy.types.ShaderNodeTree
|
||||||
:return: dict
|
:return: dict
|
||||||
"""
|
"""
|
||||||
return {
|
node_tree_data = {
|
||||||
'nodes': {node.name: dump_node(node) for node in node_tree.nodes},
|
'nodes': {node.name: dump_node(node) for node in node_tree.nodes},
|
||||||
'links': dump_links(node_tree.links)
|
'links': dump_links(node_tree.links),
|
||||||
|
'name': node_tree.name,
|
||||||
|
'type': type(node_tree).__name__
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node_tree.inputs:
|
||||||
|
node_tree_data['inputs'] = [(i.name, i.bl_socket_idname, i.identifier) for i in node_tree.inputs]
|
||||||
|
if node_tree.outputs:
|
||||||
|
node_tree_data['outputs'] = [(o.name, o.bl_socket_idname)for o in node_tree.outputs]
|
||||||
|
|
||||||
|
return node_tree_data
|
||||||
|
|
||||||
def load_shader_node_tree(node_tree_data:dict, target_node_tree:bpy.types.ShaderNodeTree)->dict:
|
def load_shader_node_tree(node_tree_data:dict, target_node_tree:bpy.types.ShaderNodeTree)->dict:
|
||||||
"""
|
"""
|
||||||
@ -205,6 +222,35 @@ def load_shader_node_tree(node_tree_data:dict, target_node_tree:bpy.types.Shader
|
|||||||
# TODO: load only required nodes
|
# TODO: load only required nodes
|
||||||
target_node_tree.nodes.clear()
|
target_node_tree.nodes.clear()
|
||||||
|
|
||||||
|
if not target_node_tree.is_property_readonly('name'):
|
||||||
|
target_node_tree.name = node_tree_data['name']
|
||||||
|
|
||||||
|
if 'inputs' in node_tree_data:
|
||||||
|
for inpt in target_node_tree.inputs:
|
||||||
|
if not [i for i in node_tree_data['inputs'] if inpt.identifier == i[2]]:
|
||||||
|
target_node_tree.inputs.remove(inpt)
|
||||||
|
|
||||||
|
for idx, socket_data in enumerate(node_tree_data['inputs']):
|
||||||
|
try:
|
||||||
|
checked_input = target_node_tree.inputs[idx]
|
||||||
|
if checked_input.name != socket_data[0]:
|
||||||
|
checked_input.name = socket_data[0]
|
||||||
|
except Exception:
|
||||||
|
target_node_tree.inputs.new(socket_data[1], socket_data[0])
|
||||||
|
|
||||||
|
if 'outputs' in node_tree_data:
|
||||||
|
for inpt in target_node_tree.outputs:
|
||||||
|
if not [o for o in node_tree_data['outputs'] if inpt.identifier == o[2]]:
|
||||||
|
target_node_tree.outputs.remove(inpt)
|
||||||
|
|
||||||
|
for idx, socket_data in enumerate(node_tree_data['outputs']):
|
||||||
|
try:
|
||||||
|
checked_outputs = target_node_tree.outputs[idx]
|
||||||
|
if checked_outputs.name != socket_data[0]:
|
||||||
|
checked_outputs.name = socket_data[0]
|
||||||
|
except Exception:
|
||||||
|
target_node_tree.outputs.new(socket_data[1], socket_data[0])
|
||||||
|
|
||||||
# Load nodes
|
# Load nodes
|
||||||
for node in node_tree_data["nodes"]:
|
for node in node_tree_data["nodes"]:
|
||||||
load_node(node_tree_data["nodes"][node], target_node_tree)
|
load_node(node_tree_data["nodes"][node], target_node_tree)
|
||||||
@ -218,8 +264,17 @@ def load_shader_node_tree(node_tree_data:dict, target_node_tree:bpy.types.Shader
|
|||||||
|
|
||||||
def get_node_tree_dependencies(node_tree: bpy.types.NodeTree) -> list:
|
def get_node_tree_dependencies(node_tree: bpy.types.NodeTree) -> list:
|
||||||
has_image = lambda node : (node.type in ['TEX_IMAGE', 'TEX_ENVIRONMENT'] and node.image)
|
has_image = lambda node : (node.type in ['TEX_IMAGE', 'TEX_ENVIRONMENT'] and node.image)
|
||||||
|
has_node_group = lambda node : (hasattr(node,'node_tree') and node.node_tree)
|
||||||
|
|
||||||
return [node.image for node in node_tree.nodes if has_image(node)]
|
deps = []
|
||||||
|
|
||||||
|
for node in node_tree.nodes:
|
||||||
|
if has_image(node):
|
||||||
|
deps.append(node.image)
|
||||||
|
elif has_node_group(node):
|
||||||
|
deps.append(node.node_tree)
|
||||||
|
|
||||||
|
return deps
|
||||||
|
|
||||||
|
|
||||||
class BlMaterial(BlDatablock):
|
class BlMaterial(BlDatablock):
|
||||||
|
47
multi_user/bl_types/bl_node_group.py
Normal file
47
multi_user/bl_types/bl_node_group.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import mathutils
|
||||||
|
|
||||||
|
from .dump_anything import Dumper, Loader, np_dump_collection, np_load_collection
|
||||||
|
from .bl_datablock import BlDatablock
|
||||||
|
from .bl_material import (dump_shader_node_tree,
|
||||||
|
load_shader_node_tree,
|
||||||
|
get_node_tree_dependencies)
|
||||||
|
|
||||||
|
class BlNodeGroup(BlDatablock):
|
||||||
|
bl_id = "node_groups"
|
||||||
|
bl_class = bpy.types.ShaderNodeTree
|
||||||
|
bl_delay_refresh = 1
|
||||||
|
bl_delay_apply = 1
|
||||||
|
bl_automatic_push = True
|
||||||
|
bl_check_common = False
|
||||||
|
bl_icon = 'NODETREE'
|
||||||
|
|
||||||
|
def _construct(self, data):
|
||||||
|
return bpy.data.node_groups.new(data["name"], data["type"])
|
||||||
|
|
||||||
|
def _load_implementation(self, data, target):
|
||||||
|
load_shader_node_tree(data, target)
|
||||||
|
|
||||||
|
def _dump_implementation(self, data, instance=None):
|
||||||
|
return dump_shader_node_tree(instance)
|
||||||
|
|
||||||
|
def _resolve_deps_implementation(self):
|
||||||
|
return get_node_tree_dependencies(self.instance)
|
@ -166,7 +166,8 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
# init the factory with supported types
|
# init the factory with supported types
|
||||||
for type in bl_types.types_to_register():
|
for type in bl_types.types_to_register():
|
||||||
type_module = getattr(bl_types, type)
|
type_module = getattr(bl_types, type)
|
||||||
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
name = [e.capitalize() for e in type.split('_')[1:]]
|
||||||
|
type_impl_name = 'Bl'+''.join(name)
|
||||||
type_module_class = getattr(type_module, type_impl_name)
|
type_module_class = getattr(type_module, type_impl_name)
|
||||||
|
|
||||||
supported_bl_types.append(type_module_class.bl_id)
|
supported_bl_types.append(type_module_class.bl_id)
|
||||||
|
@ -462,9 +462,9 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
new_db = self.supported_datablocks.add()
|
new_db = self.supported_datablocks.add()
|
||||||
|
|
||||||
type_module = getattr(bl_types, type)
|
type_module = getattr(bl_types, type)
|
||||||
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
name = [e.capitalize() for e in type.split('_')[1:]]
|
||||||
|
type_impl_name = 'Bl'+''.join(name)
|
||||||
type_module_class = getattr(type_module, type_impl_name)
|
type_module_class = getattr(type_module, type_impl_name)
|
||||||
|
|
||||||
new_db.name = type_impl_name
|
new_db.name = type_impl_name
|
||||||
new_db.type_name = type_impl_name
|
new_db.type_name = type_impl_name
|
||||||
new_db.bl_delay_refresh = type_module_class.bl_delay_refresh
|
new_db.bl_delay_refresh = type_module_class.bl_delay_refresh
|
||||||
|
Reference in New Issue
Block a user