788 lines
22 KiB
Python
788 lines
22 KiB
Python
import bpy
|
||
import mathutils
|
||
from pprint import pprint
|
||
import json
|
||
import itertools
|
||
from copy import copy
|
||
from os.path import abspath
|
||
|
||
|
||
|
||
def get_default(prop):
|
||
"""Get the default value of a bl property"""
|
||
|
||
if getattr(prop, 'is_array', False):
|
||
return list(prop.default_array)
|
||
elif hasattr(prop, 'default'):
|
||
return prop.default
|
||
|
||
|
||
def get_dumper(bl_object, fallback=None):
|
||
"""Find the right dumper type by checking inheritance"""
|
||
for dp in dumpers:
|
||
if isinstance(bl_object, dp.bl_type):
|
||
return dp
|
||
|
||
return fallback or Dumper
|
||
|
||
|
||
def get_bl_object(data):
|
||
"""Find the bl object for loading data into it depending on the type and the context"""
|
||
if data.get('_new', {}).get('type') == 'GeometryNodeTree':
|
||
return bpy.context.object.modifiers.active.node_group
|
||
|
||
|
||
def dump(ob):
|
||
"""Generic Recursive Dump, convert any object into a dict"""
|
||
Dumper.pointers.clear()
|
||
|
||
if isinstance(ob, (list, tuple)):
|
||
data = [get_dumper(o).dump(o) for o in ob]
|
||
else:
|
||
data = get_dumper(ob).dump(ob)
|
||
|
||
Dumper.pointers.clear()
|
||
|
||
return data
|
||
|
||
|
||
def load(data, bl_object=None):
|
||
"""Generic Load to create an object from a dict"""
|
||
|
||
Dumper.pointers.clear()
|
||
#print(Dumper.pointers)
|
||
|
||
if bl_object is None:
|
||
bl_object = get_bl_object(data)
|
||
|
||
dumper = get_dumper(bl_object)
|
||
dumper.load(data, bl_object)
|
||
|
||
Dumper.pointers.clear()
|
||
|
||
|
||
def set_attribute(bl_object, attr, value):
|
||
try:
|
||
setattr(bl_object, attr, value)
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
|
||
class Dumper:
|
||
pointers = {}
|
||
includes = []
|
||
excludes = ["rna_type", "bl_rna", 'id_data', 'depsgraph']
|
||
|
||
@classmethod
|
||
def properties(cls, bl_object):
|
||
if cls.includes and not cls.excludes:
|
||
return [bl_object.bl_rna.properties[p] for p in cls.includes]
|
||
else:
|
||
return [ p for p in bl_object.bl_rna.properties if not
|
||
p.identifier.startswith('bl_') and p.identifier not in cls.excludes]
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
print(f'New not implemented for data {data}')
|
||
|
||
@classmethod
|
||
def load(cls, data, bl_object=None):
|
||
if bl_object is None:
|
||
bl_object = cls.new(data)
|
||
|
||
if bl_object is None:
|
||
return
|
||
|
||
#pprint(data)
|
||
if bl_pointer := data.get('bl_pointer'):
|
||
cls.pointers[bl_pointer] = bl_object
|
||
|
||
props = cls.properties(bl_object)
|
||
for key, value in sorted(data.items(), key=lambda x: props.index(x[0]) if x[0] in props else 0):
|
||
if key.startswith('_') or key not in bl_object.bl_rna.properties:
|
||
continue
|
||
|
||
prop = bl_object.bl_rna.properties[key]
|
||
attr = getattr(bl_object, key)
|
||
|
||
if prop.type == 'COLLECTION':
|
||
dumper = PropCollection
|
||
if hasattr(attr, 'bl_rna'):
|
||
bl_type = attr.bl_rna.type_recast()
|
||
dumper = get_dumper(bl_type, fallback=PropCollection)
|
||
|
||
dumper.load(value, attr)
|
||
continue
|
||
|
||
elif prop.type == 'POINTER':
|
||
# if key == 'node_tree':
|
||
# print('--------------')
|
||
# print(bl_object, value)
|
||
# print(cls.pointers)
|
||
|
||
if isinstance(value, int): # It's a pointer
|
||
value = cls.pointers[value]
|
||
elif value is None:
|
||
set_attribute(bl_object, key, value)
|
||
|
||
else:
|
||
bl_type = prop.fixed_type.bl_rna.type_recast()
|
||
dumper = get_dumper(bl_type)
|
||
|
||
# If the pointer exist register the pointer then load data
|
||
#print('-----', value)
|
||
#pointer =
|
||
if attr is None:
|
||
attr = dumper.new(value)
|
||
|
||
dumper.load(value, attr)
|
||
#attr = getattr(bl_object, key)
|
||
#if not attr:
|
||
cls.pointers[value['bl_pointer']] = attr
|
||
|
||
if hasattr(attr, 'update'):
|
||
attr.update()
|
||
|
||
value = attr
|
||
|
||
if not prop.is_readonly:
|
||
set_attribute(bl_object, key, value)
|
||
|
||
# Some coll needs a manual update like curve mapping
|
||
if hasattr(attr, 'update'):
|
||
attr.update()
|
||
|
||
elif not prop.is_readonly:
|
||
#print(key, value)
|
||
set_attribute(bl_object, key, value)
|
||
continue
|
||
|
||
#return bl_object
|
||
|
||
@classmethod
|
||
def dump(cls, bl_object):
|
||
if isinstance(bl_object, (str, int, float, dict, list, type(None))):
|
||
return bl_object
|
||
|
||
#print('Dumping object', bl_object)
|
||
|
||
data = {"bl_pointer": bl_object.as_pointer()}
|
||
cls.pointers[bl_object.as_pointer()] = bl_object
|
||
|
||
|
||
for prop in cls.properties(bl_object):
|
||
if not hasattr(bl_object, prop.identifier):
|
||
print(f'{bl_object} has no attribute {prop.identifier}')
|
||
continue
|
||
|
||
#print(prop.identifier)
|
||
|
||
value = getattr(bl_object, prop.identifier)
|
||
|
||
# Not storing default value
|
||
if prop.identifier not in cls.includes:
|
||
if (array := getattr(prop, 'default_array', None)) and value == array:
|
||
continue
|
||
if isinstance(value, (str, int, float)) and value == prop.default:
|
||
continue
|
||
|
||
if getattr(prop, "is_array", False):
|
||
value = PropArray.dump(value)
|
||
|
||
elif prop.type == 'COLLECTION':
|
||
value = PropCollection.dump(value)
|
||
|
||
elif prop.type == 'POINTER' and value:
|
||
#if prop.identifier == 'image':
|
||
# print(bl_object, cls.pointers)
|
||
if value.as_pointer() in cls.pointers:
|
||
value = value.as_pointer()
|
||
else:
|
||
# print('Register Pointer', value.as_pointer(), value)
|
||
cls.pointers[value.as_pointer()] = value
|
||
# print(cls.pointers)
|
||
# print()
|
||
dumper = get_dumper(value)
|
||
value = dumper.dump(value)
|
||
|
||
elif bl_object.is_property_readonly(prop.identifier):
|
||
continue
|
||
|
||
else:
|
||
dumper = get_dumper(value)
|
||
value = dumper.dump(value)
|
||
|
||
data[prop.identifier] = value
|
||
|
||
return data
|
||
|
||
|
||
class PropCollection(Dumper):
|
||
bl_type = bpy.types.bpy_prop_collection
|
||
|
||
@classmethod
|
||
def dump(cls, coll):
|
||
if not len(coll):
|
||
return []
|
||
|
||
dumper = get_dumper(coll[0])
|
||
values = [dumper.dump(e) for e in coll]
|
||
|
||
# Value cannot be None
|
||
return [v for v in values if v is not None]
|
||
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
if not values:
|
||
return
|
||
|
||
dumper = None
|
||
|
||
if not hasattr(coll, 'new'): # Static collection
|
||
for item, value in zip(coll, values):
|
||
dumper = dumper or get_dumper(item)
|
||
dumper.load(value, item)
|
||
|
||
return
|
||
|
||
new_func = coll.bl_rna.functions['new']
|
||
for i, value in enumerate(values):
|
||
|
||
if value.get('_new'):
|
||
params = value['_new']
|
||
else:
|
||
params = {k: value.get(k, get_default(v)) for k, v in new_func.parameters.items()[:-1]}
|
||
|
||
# Replace arg pointer with bl object
|
||
valid_pointers = True
|
||
for param in coll.bl_rna.functions['new'].parameters:
|
||
if param.identifier not in params or param.type != 'POINTER':
|
||
continue
|
||
|
||
pointer_id = params[param.identifier]
|
||
if bl_object := cls.pointers.get(pointer_id):
|
||
params[param.identifier] = bl_object
|
||
else:
|
||
print(f'No Pointer found for param {param.identifier} of {coll}')
|
||
valid_pointers = False
|
||
|
||
if not valid_pointers:
|
||
continue
|
||
|
||
#print(param.identifier, cls.pointers[pointer_id])
|
||
|
||
try:
|
||
|
||
item = coll.new(**params)
|
||
except RuntimeError as e:
|
||
#print(e, coll.data)
|
||
#print()
|
||
try:
|
||
item = coll[i]
|
||
except IndexError as e:
|
||
#print(e, coll.data)
|
||
break
|
||
|
||
dumper = get_dumper(item)
|
||
dumper.load(value, item)#(item, value)
|
||
|
||
|
||
class PropArray(Dumper):
|
||
bl_type = bpy.types.bpy_prop_array
|
||
|
||
@classmethod
|
||
def dump(cls, array):
|
||
flat_array = []
|
||
for item in array:
|
||
if isinstance(item, (int, float)):
|
||
flat_array.append(item)
|
||
else:
|
||
flat_array.extend(cls.dump(item))
|
||
return flat_array
|
||
|
||
|
||
class NodeSocket(Dumper):
|
||
bl_type = bpy.types.NodeSocket
|
||
excludes = Dumper.excludes + ["node", "links", "display_shape", "rna_type", "link_limit"]
|
||
|
||
@classmethod
|
||
def dump(cls, socket):
|
||
if socket.is_unavailable:
|
||
return None
|
||
|
||
#cls.pointers[socket.as_pointer()] = socket
|
||
|
||
data = super().dump(socket)
|
||
|
||
#data["_id"] = socket.as_pointer()
|
||
#data.pop('name', '')
|
||
|
||
return data
|
||
|
||
|
||
class NodeGeometryRepeatOutputItems(PropCollection):
|
||
bl_type = bpy.types.NodeGeometryRepeatOutputItems
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
coll.clear()
|
||
|
||
super().load(values, coll)
|
||
|
||
|
||
class NodeLink(Dumper):
|
||
bl_type = bpy.types.NodeLink
|
||
|
||
@classmethod
|
||
def dump(cls, link):
|
||
return {"_new": {
|
||
"input": link.from_socket.as_pointer(),
|
||
"output": link.to_socket.as_pointer()
|
||
}
|
||
}
|
||
|
||
|
||
class NodeTreeInterfaceSocket(Dumper):
|
||
bl_type = bpy.types.NodeTreeInterfaceSocket
|
||
excludes = Dumper.excludes + ["parent", "interface_items"]
|
||
|
||
@classmethod
|
||
def dump(cls, socket):
|
||
#cls.pointers[socket.as_pointer()] = socket
|
||
|
||
data = super().dump(socket)
|
||
#data["_id"] = socket.as_pointer()
|
||
|
||
data['_new'] = {"name": data.get('name', '')}
|
||
|
||
if socket.item_type == 'SOCKET':
|
||
data['_new']["in_out"] = socket.in_out
|
||
|
||
|
||
# It's a real panel not the interface root
|
||
if socket.parent.parent:
|
||
data['parent'] = socket.parent.as_pointer()
|
||
|
||
return data
|
||
|
||
|
||
class NodeSockets(PropCollection):
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
|
||
#return
|
||
|
||
node_sockets = [s for s in coll if not s.is_unavailable]
|
||
for socket, value in zip(node_sockets, values):
|
||
cls.pointers[value['bl_pointer']] = socket
|
||
Dumper.load(value, socket)
|
||
# for k, v in value.items():
|
||
# if k not in socket.bl_rna.properties:
|
||
# continue
|
||
# setattr(socket, k, v)
|
||
|
||
"""
|
||
# Match Inputs Pointers
|
||
node_sockets = [s for s in coll if not s.is_unavailable]
|
||
if len(node_sockets) == len(inputs): # Match by index
|
||
super().load({"inputs": inputs}, node)
|
||
for socket, value in zip(node_sockets, coll):
|
||
cls.pointers[value['_id']] = socket
|
||
else: # Match by name
|
||
print(f'Match Inputs by Name for node {node}')
|
||
for socket in node_sockets:
|
||
index = next((i for i, v in enumerate(inputs) if v['name'] == socket.name), None)
|
||
if index is None:
|
||
continue
|
||
|
||
value = inputs[index]
|
||
print(socket, value)
|
||
cls.pointers[value['_id']] = socket
|
||
|
||
Dumper.load(value, socket)
|
||
del inputs[index]
|
||
"""
|
||
|
||
|
||
class NodeInputs(NodeSockets):
|
||
bl_type = bpy.types.NodeInputs
|
||
|
||
|
||
class NodeOutputs(NodeSockets):
|
||
bl_type = bpy.types.NodeOutputs
|
||
|
||
|
||
class Node(Dumper):
|
||
bl_type = bpy.types.Node
|
||
excludes = Dumper.excludes + ["dimensions", "height", "internal_links", "paired_output"]
|
||
|
||
@classmethod
|
||
def dump(cls, node=None):
|
||
#cls.pointers[node.as_pointer()] = node
|
||
|
||
data = super().dump(node)
|
||
#data["_id"] = node.as_pointer()
|
||
data["_new"] = {"type": node.bl_rna.identifier} # 'node_tree': node.id_data.as_pointer()
|
||
|
||
if paired_output := getattr(node, "paired_output", None):
|
||
data["_pair_with_output"] = paired_output.as_pointer()
|
||
|
||
#if node.parent:
|
||
# data['location'] -= Vector()node.parent.location
|
||
|
||
return data
|
||
|
||
@classmethod
|
||
def load(cls, data, node):
|
||
if node is None:
|
||
return
|
||
#cls.pointers[data['bl_pointer']] = node
|
||
|
||
inputs = copy(data.pop('inputs', []))
|
||
outputs = copy(data.pop('outputs', []))
|
||
|
||
super().load(data, node)
|
||
|
||
data['inputs'] = inputs
|
||
data['outputs'] = outputs
|
||
|
||
# Loading input and outputs after the properties
|
||
super().load({"inputs": inputs, "outputs": outputs}, node)
|
||
|
||
if node.parent:
|
||
node.location += node.parent.location
|
||
|
||
#if node.type != 'FRAME':
|
||
# node.location.y -= 500
|
||
|
||
|
||
class CompositorNodeGlare(Node):
|
||
bl_type = bpy.types.CompositorNodeGlare
|
||
|
||
includes = ["quality"]
|
||
|
||
|
||
class NodeTreeInterface(Dumper):
|
||
bl_type = bpy.types.NodeTreeInterface
|
||
|
||
@classmethod
|
||
def load(cls, data, interface):
|
||
|
||
print('Load Interface')
|
||
|
||
for value in data.get('items_tree', []):
|
||
item_type = value.get('item_type', 'SOCKET')
|
||
if item_type == 'SOCKET':
|
||
item = interface.new_socket(**value['_new'])
|
||
elif item_type == 'PANEL':
|
||
#print(value['_new'])
|
||
item = interface.new_panel(**value['_new'])
|
||
|
||
NodeTreeInterfaceSocket.load(value, item)
|
||
|
||
interface.active_index = data.get('active_index', 0)
|
||
|
||
|
||
class Nodes(PropCollection):
|
||
bl_type = bpy.types.Nodes
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
super().load(values, coll)
|
||
|
||
# Pair zone input and output
|
||
for node_data in values:
|
||
if paired_output_id := node_data.get('_pair_with_output', None):
|
||
node = cls.pointers[node_data['bl_pointer']]
|
||
node.pair_with_output(cls.pointers[paired_output_id])
|
||
|
||
#print(node, node_data['outputs'])
|
||
|
||
Dumper.load({"inputs": node_data['inputs'], "outputs": node_data['outputs']}, node)
|
||
|
||
|
||
class NodeTree(Dumper):
|
||
bl_type = bpy.types.NodeTree
|
||
excludes = []
|
||
includes = ["name", "interface", "nodes", "links"]
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
if link := data.get('_link'):
|
||
with bpy.data.libraries.load(link['filepath'], link=True) as (data_from, data_to):
|
||
setattr(data_to, link['data_type'], [link['name']])
|
||
return getattr(data_to, link['data_type'])[0]
|
||
|
||
return bpy.data.node_groups.new(**data["_new"])
|
||
|
||
@classmethod
|
||
def dump(cls, node_tree):
|
||
if node_tree.library:
|
||
data = {'bl_pointer': node_tree.as_pointer()}
|
||
filepath = abspath(bpy.path.abspath(node_tree.library.filepath, library=node_tree.library.library))
|
||
data["_link"] = {"filepath": filepath, "data_type": 'node_groups', 'name': node_tree.name}
|
||
else:
|
||
data = super().dump(node_tree)
|
||
data["_new"] = {"type": node_tree.bl_rna.identifier, 'name': node_tree.name}
|
||
|
||
return data
|
||
|
||
|
||
class Points(PropCollection):
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
new_func = coll.bl_rna.functions['new']
|
||
params = {k: get_default(v)+1.1 for k, v in new_func.parameters.items()[:-1]}
|
||
|
||
# Match the same number of elements in collection
|
||
if len(values) > len(coll):
|
||
for _ in range(len(values) - len(coll)):
|
||
coll.new(**params)
|
||
|
||
for i, value in enumerate(values):
|
||
Dumper.load(value, coll[i])
|
||
#for k, v in value.items():
|
||
#setattr(coll[i], k, v)
|
||
|
||
|
||
class CurveMapPoints(Points):
|
||
bl_type = bpy.types.CurveMapPoints
|
||
|
||
|
||
class ColorRampElements(Points):
|
||
bl_type = bpy.types.ColorRampElements
|
||
|
||
|
||
class CompositorNodeOutputFileLayerSlots(PropCollection):
|
||
bl_type = bpy.types.CompositorNodeOutputFileLayerSlots
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
coll.clear()
|
||
|
||
super().load(values, coll)
|
||
|
||
|
||
class CompositorNodeOutputFileFileSlots(PropCollection):
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
coll.clear()
|
||
|
||
super().load(values, coll)
|
||
|
||
|
||
class AOVs(PropCollection):
|
||
bl_type = bpy.types.AOVs
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
for value in values:
|
||
aov = coll.get(value['name'])
|
||
|
||
if not aov:
|
||
aov = coll.add()
|
||
|
||
Dumper.load(value, aov)
|
||
|
||
|
||
class Image(Dumper):
|
||
bl_type = bpy.types.Image
|
||
|
||
excludes = []
|
||
includes = ['name', 'filepath']
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
# image = next(( img for img in bpy.data.images if not img.library
|
||
# and img.filepath == data['filepath']), None)
|
||
|
||
# if image is None:
|
||
# image = bpy.data.images.load(data['filepath'])
|
||
|
||
return bpy.data.images.load(data['filepath'], check_existing=True)
|
||
|
||
|
||
class Material(Dumper):
|
||
bl_type = bpy.types.Material
|
||
|
||
excludes = Dumper.excludes + ['preview', "original"]
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
material = bpy.data.materials.get(data.get('name', ''))
|
||
|
||
if material is None:
|
||
material = bpy.data.materials.new(data['name'])
|
||
|
||
return material
|
||
|
||
|
||
class Object(Dumper):
|
||
bl_type = bpy.types.Object
|
||
excludes = []
|
||
includes = ['name']
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
if name := data.get('name'):
|
||
return bpy.data.objects.get(name)
|
||
|
||
|
||
class Scene(Dumper):
|
||
bl_type = bpy.types.Scene
|
||
excludes = []
|
||
includes = ['name']
|
||
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
if scene := bpy.data.scenes.get(data.get('name', '')):
|
||
return scene
|
||
|
||
return bpy.data.scenes.new(name=data.get('name', ''))
|
||
|
||
"""
|
||
@classmethod
|
||
def dump(cls, scene):
|
||
view_layer = scene.view_layers[node.layer]
|
||
view_layer_data = ViewLayer.dump(view_layer)
|
||
|
||
return {
|
||
'bl_pointer': scene.as_pointer(),
|
||
'name': scene.name,
|
||
'render' : {'bl_pointer': scene.render.as_pointer(), "engine": scene.render.engine},
|
||
'view_layers': [view_layer_data]
|
||
}
|
||
"""
|
||
|
||
class Collection(Dumper):
|
||
bl_type = bpy.types.Collection
|
||
includes = ['name']
|
||
excludes = []
|
||
|
||
@classmethod
|
||
def new(cls, data):
|
||
if name := data.get('name'):
|
||
return bpy.data.collections.get(name)
|
||
|
||
# @classmethod
|
||
# def dump(cls, data):
|
||
# data = super().dump(data)
|
||
|
||
# data['render'] = {"engine": scene.render.engine}
|
||
# return data
|
||
|
||
|
||
class CompositorNodeRLayers(Node):
|
||
bl_type = bpy.types.CompositorNodeRLayers
|
||
|
||
excludes = Dumper.excludes + ['scene']
|
||
|
||
@classmethod
|
||
def load(cls, data, node):
|
||
|
||
#print('load CompositorNodeRLayers')
|
||
|
||
scene_data = data.pop('scene')
|
||
#print(scene_data)
|
||
layer = data.pop('layer')
|
||
scene = Scene.new(scene_data)
|
||
Scene.load(scene_data, scene)
|
||
|
||
node.scene = scene
|
||
node.layer = layer
|
||
|
||
super().load(data, node)
|
||
|
||
# Resetter the view_layer because it might have been created
|
||
# with the scene attr in the dictionnary and nor available yet
|
||
|
||
#print(bpy.)
|
||
|
||
|
||
|
||
@classmethod
|
||
def dump(cls, node):
|
||
# Add scene and viewlayer passes
|
||
data = super().dump(node)
|
||
|
||
#if
|
||
|
||
view_layer = node.scene.view_layers[node.layer]
|
||
view_layer_data = ViewLayer.dump(view_layer)
|
||
|
||
'''
|
||
view_layer_data = {
|
||
"name": view_layer.name}
|
||
properties = {p.name: p for p in view_layer.bl_rna.properties}
|
||
for prop in view_layer.bl_rna:
|
||
if prop.identifier.startswith('use_pass'):
|
||
view_layer_data[prop.identifier]
|
||
'''
|
||
|
||
#cls.pointers[bl_object.as_pointer()] = bl_object
|
||
|
||
data['scene'] = {
|
||
'bl_pointer': node.scene.as_pointer(),
|
||
'name': node.scene.name,
|
||
'render' : {'bl_pointer': node.scene.render.as_pointer(), "engine": node.scene.render.engine},
|
||
'view_layers': [view_layer_data]
|
||
}
|
||
|
||
return data
|
||
|
||
|
||
class ViewLayer(Dumper):
|
||
bl_type = bpy.types.ViewLayer
|
||
excludes = Dumper.excludes + ['freestyle_settings', 'eevee', 'cycles', 'active_layer_collection',
|
||
'active_aov', 'active_lightgroup_index', 'layer_collection', 'lightgroups', 'material_override',
|
||
'objects', 'use']
|
||
#includes = ['name']
|
||
|
||
|
||
class ViewLayers(PropCollection):
|
||
bl_type = bpy.types.ViewLayers
|
||
|
||
@classmethod
|
||
def load(cls, values, coll):
|
||
#print('LOAD VIEWLAYERS', values)
|
||
for value in values:
|
||
view_layer = coll.get(value['name'])
|
||
|
||
if view_layer is None:
|
||
view_layer = coll.new(value['name'])
|
||
|
||
Dumper.load(value, view_layer)
|
||
|
||
|
||
dumpers = [
|
||
CompositorNodeRLayers,
|
||
CompositorNodeGlare,
|
||
Node,
|
||
NodeSocket,
|
||
NodeTree,
|
||
NodeLink,
|
||
NodeTreeInterface,
|
||
NodeTreeInterfaceSocket,
|
||
NodeGeometryRepeatOutputItems,
|
||
Image,
|
||
Material,
|
||
Object,
|
||
Scene,
|
||
Collection,
|
||
ViewLayer,
|
||
CurveMapPoints,
|
||
ColorRampElements,
|
||
NodeInputs,
|
||
NodeOutputs,
|
||
Nodes,
|
||
ViewLayers,
|
||
PropCollection,
|
||
AOVs,
|
||
PropArray,
|
||
CompositorNodeOutputFileLayerSlots,
|
||
CompositorNodeOutputFileFileSlots,
|
||
] |