Revert "Merge branch '45-vse-support' into 'master'"
This reverts merge request !68
This commit is contained in:
parent
d42ed789e7
commit
761fa26ed7
@ -36,7 +36,6 @@ Currently, not all data-block are supported for replication over the wire. The f
|
|||||||
| image | ✔️ | |
|
| image | ✔️ | |
|
||||||
| mesh | ✔️ | |
|
| mesh | ✔️ | |
|
||||||
| material | ✔️ | |
|
| material | ✔️ | |
|
||||||
| node_groups | ❗ | Material only |
|
|
||||||
| metaball | ✔️ | |
|
| metaball | ✔️ | |
|
||||||
| object | ✔️ | |
|
| object | ✔️ | |
|
||||||
| texts | ✔️ | |
|
| texts | ✔️ | |
|
||||||
@ -49,7 +48,7 @@ Currently, not all data-block are supported for replication over the wire. The f
|
|||||||
| volumes | ❌ | |
|
| volumes | ❌ | |
|
||||||
| particles | ❌ | [On-going](https://gitlab.com/slumber/multi-user/-/issues/24) |
|
| particles | ❌ | [On-going](https://gitlab.com/slumber/multi-user/-/issues/24) |
|
||||||
| speakers | ❗ | [Partial](https://gitlab.com/slumber/multi-user/-/issues/65) |
|
| speakers | ❗ | [Partial](https://gitlab.com/slumber/multi-user/-/issues/65) |
|
||||||
| vse | ❗ | Mask and Clip not supported yet |
|
| vse | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/45) |
|
||||||
| physics | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/45) |
|
| physics | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/45) |
|
||||||
| libraries | ❗ | Partial |
|
| libraries | ❗ | Partial |
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 70 KiB |
@ -251,14 +251,6 @@ it draw users related information in your viewport such as:
|
|||||||
The presence overlay panel (see image above) allow you to enable/disable
|
The presence overlay panel (see image above) allow you to enable/disable
|
||||||
various drawn parts via the following flags:
|
various drawn parts via the following flags:
|
||||||
|
|
||||||
- **Show session statut**: display the session status in the viewport
|
|
||||||
|
|
||||||
.. figure:: img/quickstart_status.png
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
- **Text scale**: session status text size
|
|
||||||
- **Vertical/Horizontal position**: session position in the viewport
|
|
||||||
|
|
||||||
- **Show selected objects**: display other users current selection
|
- **Show selected objects**: display other users current selection
|
||||||
- **Show users**: display users current viewpoint
|
- **Show users**: display users current viewpoint
|
||||||
- **Show different scenes**: display users working on other scenes
|
- **Show different scenes**: display users working on other scenes
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Multi-User",
|
"name": "Multi-User",
|
||||||
"author": "Swann Martinez",
|
"author": "Swann Martinez",
|
||||||
"version": (0, 2, 0),
|
"version": (0, 1, 1),
|
||||||
"description": "Enable real-time collaborative workflow inside blender",
|
"description": "Enable real-time collaborative workflow inside blender",
|
||||||
"blender": (2, 82, 0),
|
"blender": (2, 82, 0),
|
||||||
"location": "3D View > Sidebar > Multi-User tab",
|
"location": "3D View > Sidebar > Multi-User tab",
|
||||||
@ -44,7 +44,7 @@ from . import environment
|
|||||||
|
|
||||||
|
|
||||||
DEPENDENCIES = {
|
DEPENDENCIES = {
|
||||||
("replication", '0.1.9'),
|
("replication", '0.1.3'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,9 +37,7 @@ __all__ = [
|
|||||||
'bl_speaker',
|
'bl_speaker',
|
||||||
'bl_font',
|
'bl_font',
|
||||||
'bl_sound',
|
'bl_sound',
|
||||||
'bl_file',
|
'bl_file'
|
||||||
'bl_sequencer',
|
|
||||||
'bl_node_group'
|
|
||||||
] # Order here defines execution order
|
] # Order here defines execution order
|
||||||
|
|
||||||
from . import *
|
from . import *
|
||||||
|
@ -42,7 +42,7 @@ KEYFRAME = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def dump_fcurve(fcurve: bpy.types.FCurve, use_numpy: bool = True) -> dict:
|
def dump_fcurve(fcurve: bpy.types.FCurve, use_numpy:bool =True) -> dict:
|
||||||
""" Dump a sigle curve to a dict
|
""" Dump a sigle curve to a dict
|
||||||
|
|
||||||
:arg fcurve: fcurve to dump
|
:arg fcurve: fcurve to dump
|
||||||
@ -59,7 +59,7 @@ def dump_fcurve(fcurve: bpy.types.FCurve, use_numpy: bool = True) -> dict:
|
|||||||
|
|
||||||
if use_numpy:
|
if use_numpy:
|
||||||
points = fcurve.keyframe_points
|
points = fcurve.keyframe_points
|
||||||
fcurve_data['keyframes_count'] = len(fcurve.keyframe_points)
|
fcurve_data['keyframes_count'] = len(fcurve.keyframe_points)
|
||||||
fcurve_data['keyframe_points'] = np_dump_collection(points, KEYFRAME)
|
fcurve_data['keyframe_points'] = np_dump_collection(points, KEYFRAME)
|
||||||
|
|
||||||
else: # Legacy method
|
else: # Legacy method
|
||||||
@ -92,8 +92,7 @@ def load_fcurve(fcurve_data, fcurve):
|
|||||||
|
|
||||||
if use_numpy:
|
if use_numpy:
|
||||||
keyframe_points.add(fcurve_data['keyframes_count'])
|
keyframe_points.add(fcurve_data['keyframes_count'])
|
||||||
np_load_collection(
|
np_load_collection(fcurve_data["keyframe_points"], keyframe_points, KEYFRAME)
|
||||||
fcurve_data["keyframe_points"], keyframe_points, KEYFRAME)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# paste dumped keyframes
|
# paste dumped keyframes
|
||||||
@ -154,11 +153,7 @@ class BlAction(BlDatablock):
|
|||||||
dumped_data_path, index=dumped_array_index)
|
dumped_data_path, index=dumped_array_index)
|
||||||
|
|
||||||
load_fcurve(dumped_fcurve, fcurve)
|
load_fcurve(dumped_fcurve, fcurve)
|
||||||
|
target.id_root = data['id_root']
|
||||||
id_root = data.get('id_root')
|
|
||||||
|
|
||||||
if id_root:
|
|
||||||
target.id_root = id_root
|
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
dumper = Dumper()
|
dumper = Dumper()
|
||||||
|
@ -48,15 +48,12 @@ class BlCamera(BlDatablock):
|
|||||||
|
|
||||||
background_images = data.get('background_images')
|
background_images = data.get('background_images')
|
||||||
|
|
||||||
target.background_images.clear()
|
|
||||||
|
|
||||||
if background_images:
|
if background_images:
|
||||||
|
target.background_images.clear()
|
||||||
for img_name, img_data in background_images.items():
|
for img_name, img_data in background_images.items():
|
||||||
img_id = img_data.get('image')
|
target_img = target.background_images.new()
|
||||||
if img_id:
|
target_img.image = bpy.data.images[img_name]
|
||||||
target_img = target.background_images.new()
|
loader.load(target_img, img_data)
|
||||||
target_img.image = bpy.data.images[img_id]
|
|
||||||
loader.load(target_img, img_data)
|
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
assert(instance)
|
assert(instance)
|
||||||
|
@ -71,15 +71,6 @@ def load_collection_childrens(dumped_childrens, collection):
|
|||||||
if child_collection.uuid not in dumped_childrens:
|
if child_collection.uuid not in dumped_childrens:
|
||||||
collection.children.unlink(child_collection)
|
collection.children.unlink(child_collection)
|
||||||
|
|
||||||
def resolve_collection_dependencies(collection):
|
|
||||||
deps = []
|
|
||||||
|
|
||||||
for child in collection.children:
|
|
||||||
deps.append(child)
|
|
||||||
for object in collection.objects:
|
|
||||||
deps.append(object)
|
|
||||||
|
|
||||||
return deps
|
|
||||||
|
|
||||||
class BlCollection(BlDatablock):
|
class BlCollection(BlDatablock):
|
||||||
bl_id = "collections"
|
bl_id = "collections"
|
||||||
@ -133,4 +124,11 @@ class BlCollection(BlDatablock):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def _resolve_deps_implementation(self):
|
def _resolve_deps_implementation(self):
|
||||||
return resolve_collection_dependencies(self.instance)
|
deps = []
|
||||||
|
|
||||||
|
for child in self.instance.children:
|
||||||
|
deps.append(child)
|
||||||
|
for object in self.instance.objects:
|
||||||
|
deps.append(object)
|
||||||
|
|
||||||
|
return deps
|
||||||
|
@ -21,7 +21,7 @@ from collections.abc import Iterable
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
from replication.constants import DIFF_BINARY, DIFF_JSON, UP
|
from replication.constants import DIFF_BINARY, UP
|
||||||
from replication.data import ReplicatedDatablock
|
from replication.data import ReplicatedDatablock
|
||||||
|
|
||||||
from .. import utils
|
from .. import utils
|
||||||
@ -92,6 +92,7 @@ def load_driver(target_datablock, src_driver):
|
|||||||
def get_datablock_from_uuid(uuid, default, ignore=[]):
|
def get_datablock_from_uuid(uuid, default, ignore=[]):
|
||||||
if not uuid:
|
if not uuid:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
for category in dir(bpy.data):
|
for category in dir(bpy.data):
|
||||||
root = getattr(bpy.data, category)
|
root = getattr(bpy.data, category)
|
||||||
if isinstance(root, Iterable) and category not in ignore:
|
if isinstance(root, Iterable) and category not in ignore:
|
||||||
@ -122,15 +123,12 @@ class BlDatablock(ReplicatedDatablock):
|
|||||||
# TODO: use is_library_indirect
|
# TODO: use is_library_indirect
|
||||||
self.is_library = (instance and hasattr(instance, 'library') and
|
self.is_library = (instance and hasattr(instance, 'library') and
|
||||||
instance.library) or \
|
instance.library) or \
|
||||||
(hasattr(self,'data') and self.data and 'library' in self.data)
|
(self.data and 'library' in self.data)
|
||||||
|
|
||||||
if instance and hasattr(instance, 'uuid'):
|
if instance and hasattr(instance, 'uuid'):
|
||||||
instance.uuid = self.uuid
|
instance.uuid = self.uuid
|
||||||
|
|
||||||
if logging.getLogger().level == logging.DEBUG:
|
self.diff_method = DIFF_BINARY
|
||||||
self.diff_method = DIFF_JSON
|
|
||||||
else:
|
|
||||||
self.diff_method = DIFF_BINARY
|
|
||||||
|
|
||||||
def resolve(self):
|
def resolve(self):
|
||||||
datablock_ref = None
|
datablock_ref = None
|
||||||
@ -219,7 +217,7 @@ class BlDatablock(ReplicatedDatablock):
|
|||||||
if not self.is_library:
|
if not self.is_library:
|
||||||
dependencies.extend(self._resolve_deps_implementation())
|
dependencies.extend(self._resolve_deps_implementation())
|
||||||
|
|
||||||
logging.debug(f"{self.instance} dependencies: {dependencies}")
|
logging.debug(f"{self.instance.name} dependencies: {dependencies}")
|
||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
def _resolve_deps_implementation(self):
|
def _resolve_deps_implementation(self):
|
||||||
|
@ -21,8 +21,6 @@ import mathutils
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from .dump_anything import Loader, Dumper
|
from .dump_anything import Loader, Dumper
|
||||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
||||||
|
|
||||||
@ -39,34 +37,28 @@ def load_node(node_data, node_tree):
|
|||||||
"""
|
"""
|
||||||
loader = Loader()
|
loader = Loader()
|
||||||
target_node = node_tree.nodes.new(type=node_data["bl_idname"])
|
target_node = node_tree.nodes.new(type=node_data["bl_idname"])
|
||||||
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)
|
||||||
|
|
||||||
if node_tree_uuid:
|
for input in node_data["inputs"]:
|
||||||
target_node.node_tree = get_datablock_from_uuid(node_tree_uuid, None)
|
if hasattr(target_node.inputs[input], "default_value"):
|
||||||
|
try:
|
||||||
|
target_node.inputs[input].default_value = node_data["inputs"][input]["default_value"]
|
||||||
|
except:
|
||||||
|
logging.error(
|
||||||
|
f"Material {input} parameter not supported, skipping")
|
||||||
|
|
||||||
inputs = node_data.get('inputs')
|
for output in node_data["outputs"]:
|
||||||
if inputs:
|
if hasattr(target_node.outputs[output], "default_value"):
|
||||||
for idx, inpt in enumerate(inputs):
|
try:
|
||||||
if hasattr(target_node.inputs[idx], "default_value"):
|
target_node.outputs[output].default_value = node_data["outputs"][output]["default_value"]
|
||||||
try:
|
except:
|
||||||
target_node.inputs[idx].default_value = inpt["default_value"]
|
logging.error(
|
||||||
except:
|
f"Material {output} parameter not supported, skipping")
|
||||||
logging.error(f"Material input {inpt.keys()} parameter not supported, skipping")
|
|
||||||
|
|
||||||
outputs = node_data.get('outputs')
|
|
||||||
if outputs:
|
|
||||||
for idx, output in enumerate(outputs):
|
|
||||||
if hasattr(target_node.outputs[idx], "default_value"):
|
|
||||||
try:
|
|
||||||
target_node.outputs[idx].default_value = output["default_value"]
|
|
||||||
except:
|
|
||||||
logging.error(f"Material output {output.keys()} parameter not supported, skipping")
|
|
||||||
|
|
||||||
|
|
||||||
def load_links(links_data, node_tree):
|
def load_links(links_data, node_tree):
|
||||||
@ -150,20 +142,24 @@ def dump_node(node):
|
|||||||
dumped_node = node_dumper.dump(node)
|
dumped_node = node_dumper.dump(node)
|
||||||
|
|
||||||
if hasattr(node, 'inputs'):
|
if hasattr(node, 'inputs'):
|
||||||
dumped_node['inputs'] = []
|
dumped_node['inputs'] = {}
|
||||||
|
|
||||||
io_dumper = Dumper()
|
for i in node.inputs:
|
||||||
io_dumper.depth = 2
|
input_dumper = Dumper()
|
||||||
io_dumper.include_filter = ["default_value"]
|
input_dumper.depth = 2
|
||||||
|
input_dumper.include_filter = ["default_value"]
|
||||||
|
|
||||||
for idx, inpt in enumerate(node.inputs):
|
if hasattr(i, 'default_value'):
|
||||||
if hasattr(inpt, 'default_value'):
|
dumped_node['inputs'][i.name] = input_dumper.dump(i)
|
||||||
dumped_node['inputs'].append(io_dumper.dump(inpt))
|
|
||||||
|
|
||||||
dumped_node['outputs'] = []
|
dumped_node['outputs'] = {}
|
||||||
for idx, output in enumerate(node.outputs):
|
for i in node.outputs:
|
||||||
if hasattr(output, 'default_value'):
|
output_dumper = Dumper()
|
||||||
dumped_node['outputs'].append(io_dumper.dump(output))
|
output_dumper.depth = 2
|
||||||
|
output_dumper.include_filter = ["default_value"]
|
||||||
|
|
||||||
|
if hasattr(i, 'default_value'):
|
||||||
|
dumped_node['outputs'][i.name] = output_dumper.dump(i)
|
||||||
|
|
||||||
if hasattr(node, 'color_ramp'):
|
if hasattr(node, 'color_ramp'):
|
||||||
ramp_dumper = Dumper()
|
ramp_dumper = Dumper()
|
||||||
@ -186,126 +182,13 @@ 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
|
||||||
|
|
||||||
|
|
||||||
def dump_shader_node_tree(node_tree: bpy.types.ShaderNodeTree) -> dict:
|
|
||||||
""" Dump a shader node_tree to a dict including links and nodes
|
|
||||||
|
|
||||||
:arg node_tree: dumped shader node tree
|
|
||||||
:type node_tree: bpy.types.ShaderNodeTree
|
|
||||||
:return: dict
|
|
||||||
"""
|
|
||||||
node_tree_data = {
|
|
||||||
'nodes': {node.name: dump_node(node) for node in node_tree.nodes},
|
|
||||||
'links': dump_links(node_tree.links),
|
|
||||||
'name': node_tree.name,
|
|
||||||
'type': type(node_tree).__name__
|
|
||||||
}
|
|
||||||
|
|
||||||
for socket_id in ['inputs', 'outputs']:
|
|
||||||
socket_collection = getattr(node_tree, socket_id)
|
|
||||||
node_tree_data[socket_id] = dump_node_tree_sockets(socket_collection)
|
|
||||||
|
|
||||||
return node_tree_data
|
|
||||||
|
|
||||||
|
|
||||||
def dump_node_tree_sockets(sockets: bpy.types.Collection)->dict:
|
|
||||||
""" dump sockets of a shader_node_tree
|
|
||||||
|
|
||||||
:arg target_node_tree: target node_tree
|
|
||||||
:type target_node_tree: bpy.types.NodeTree
|
|
||||||
:arg socket_id: socket identifer
|
|
||||||
:type socket_id: str
|
|
||||||
:return: dict
|
|
||||||
"""
|
|
||||||
sockets_data = []
|
|
||||||
for socket in sockets:
|
|
||||||
try:
|
|
||||||
socket_uuid = socket['uuid']
|
|
||||||
except Exception:
|
|
||||||
socket_uuid = str(uuid4())
|
|
||||||
socket['uuid'] = socket_uuid
|
|
||||||
|
|
||||||
sockets_data.append((socket.name, socket.bl_socket_idname, socket_uuid))
|
|
||||||
|
|
||||||
return sockets_data
|
|
||||||
|
|
||||||
def load_node_tree_sockets(sockets: bpy.types.Collection,
|
|
||||||
sockets_data: dict):
|
|
||||||
""" load sockets of a shader_node_tree
|
|
||||||
|
|
||||||
:arg target_node_tree: target node_tree
|
|
||||||
:type target_node_tree: bpy.types.NodeTree
|
|
||||||
:arg socket_id: socket identifer
|
|
||||||
:type socket_id: str
|
|
||||||
:arg socket_data: dumped socket data
|
|
||||||
:type socket_data: dict
|
|
||||||
"""
|
|
||||||
# Check for removed sockets
|
|
||||||
for socket in sockets:
|
|
||||||
if not [s for s in sockets_data if socket['uuid'] == s[2]]:
|
|
||||||
sockets.remove(socket)
|
|
||||||
|
|
||||||
# Check for new sockets
|
|
||||||
for idx, socket_data in enumerate(sockets_data):
|
|
||||||
try:
|
|
||||||
checked_socket = sockets[idx]
|
|
||||||
if checked_socket.name != socket_data[0]:
|
|
||||||
checked_socket.name = socket_data[0]
|
|
||||||
except Exception:
|
|
||||||
s = sockets.new(socket_data[1], socket_data[0])
|
|
||||||
s['uuid'] = socket_data[2]
|
|
||||||
|
|
||||||
|
|
||||||
def load_shader_node_tree(node_tree_data:dict, target_node_tree:bpy.types.ShaderNodeTree)->dict:
|
|
||||||
"""Load a shader node_tree from dumped data
|
|
||||||
|
|
||||||
:arg node_tree_data: dumped node data
|
|
||||||
:type node_tree_data: dict
|
|
||||||
:arg target_node_tree: target node_tree
|
|
||||||
:type target_node_tree: bpy.types.NodeTree
|
|
||||||
"""
|
|
||||||
# TODO: load only required nodes
|
|
||||||
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:
|
|
||||||
socket_collection = getattr(target_node_tree, 'inputs')
|
|
||||||
load_node_tree_sockets(socket_collection, node_tree_data['inputs'])
|
|
||||||
|
|
||||||
if 'outputs' in node_tree_data:
|
|
||||||
socket_collection = getattr(target_node_tree, 'outputs')
|
|
||||||
load_node_tree_sockets(socket_collection,node_tree_data['outputs'])
|
|
||||||
|
|
||||||
# Load nodes
|
|
||||||
for node in node_tree_data["nodes"]:
|
|
||||||
load_node(node_tree_data["nodes"][node], target_node_tree)
|
|
||||||
|
|
||||||
# TODO: load only required nodes links
|
|
||||||
# Load nodes links
|
|
||||||
target_node_tree.links.clear()
|
|
||||||
|
|
||||||
load_links(node_tree_data["links"], target_node_tree)
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
deps = []
|
return [node.image for node in node_tree.nodes if has_image(node)]
|
||||||
|
|
||||||
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):
|
||||||
@ -336,7 +219,16 @@ class BlMaterial(BlDatablock):
|
|||||||
if target.node_tree is None:
|
if target.node_tree is None:
|
||||||
target.use_nodes = True
|
target.use_nodes = True
|
||||||
|
|
||||||
load_shader_node_tree(data['node_tree'], target.node_tree)
|
target.node_tree.nodes.clear()
|
||||||
|
|
||||||
|
# Load nodes
|
||||||
|
for node in data["node_tree"]["nodes"]:
|
||||||
|
load_node(data["node_tree"]["nodes"][node], target.node_tree)
|
||||||
|
|
||||||
|
# Load nodes links
|
||||||
|
target.node_tree.links.clear()
|
||||||
|
|
||||||
|
load_links(data["node_tree"]["links"], target.node_tree)
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
assert(instance)
|
assert(instance)
|
||||||
@ -367,7 +259,15 @@ class BlMaterial(BlDatablock):
|
|||||||
]
|
]
|
||||||
data = mat_dumper.dump(instance)
|
data = mat_dumper.dump(instance)
|
||||||
|
|
||||||
if instance.is_grease_pencil:
|
if instance.use_nodes:
|
||||||
|
nodes = {}
|
||||||
|
data["node_tree"] = {}
|
||||||
|
for node in instance.node_tree.nodes:
|
||||||
|
nodes[node.name] = dump_node(node)
|
||||||
|
data["node_tree"]['nodes'] = nodes
|
||||||
|
|
||||||
|
data["node_tree"]["links"] = dump_links(instance.node_tree.links)
|
||||||
|
elif instance.is_grease_pencil:
|
||||||
gp_mat_dumper = Dumper()
|
gp_mat_dumper = Dumper()
|
||||||
gp_mat_dumper.depth = 3
|
gp_mat_dumper.depth = 3
|
||||||
|
|
||||||
@ -399,9 +299,6 @@ class BlMaterial(BlDatablock):
|
|||||||
# 'fill_image',
|
# 'fill_image',
|
||||||
]
|
]
|
||||||
data['grease_pencil'] = gp_mat_dumper.dump(instance.grease_pencil)
|
data['grease_pencil'] = gp_mat_dumper.dump(instance.grease_pencil)
|
||||||
elif instance.use_nodes:
|
|
||||||
data['node_tree'] = dump_shader_node_tree(instance.node_tree)
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _resolve_deps_implementation(self):
|
def _resolve_deps_implementation(self):
|
||||||
|
@ -25,7 +25,7 @@ import numpy as np
|
|||||||
from .dump_anything import Dumper, Loader, np_load_collection_primitives, np_dump_collection_primitive, np_load_collection, np_dump_collection
|
from .dump_anything import Dumper, Loader, np_load_collection_primitives, np_dump_collection_primitive, np_load_collection, np_dump_collection
|
||||||
from replication.constants import DIFF_BINARY
|
from replication.constants import DIFF_BINARY
|
||||||
from replication.exception import ContextError
|
from replication.exception import ContextError
|
||||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
from .bl_datablock import BlDatablock
|
||||||
|
|
||||||
VERTICE = ['co']
|
VERTICE = ['co']
|
||||||
|
|
||||||
@ -70,17 +70,8 @@ class BlMesh(BlDatablock):
|
|||||||
# MATERIAL SLOTS
|
# MATERIAL SLOTS
|
||||||
target.materials.clear()
|
target.materials.clear()
|
||||||
|
|
||||||
for mat_uuid, mat_name in data["material_list"]:
|
for m in data["material_list"]:
|
||||||
mat_ref = None
|
target.materials.append(bpy.data.materials[m])
|
||||||
if mat_uuid is not None:
|
|
||||||
mat_ref = get_datablock_from_uuid(mat_uuid, None)
|
|
||||||
else:
|
|
||||||
mat_ref = bpy.data.materials.get(mat_name, None)
|
|
||||||
|
|
||||||
if mat_ref is None:
|
|
||||||
raise Exception("Material doesn't exist")
|
|
||||||
|
|
||||||
target.materials.append(mat_ref)
|
|
||||||
|
|
||||||
# CLEAR GEOMETRY
|
# CLEAR GEOMETRY
|
||||||
if target.vertices:
|
if target.vertices:
|
||||||
@ -172,7 +163,12 @@ class BlMesh(BlDatablock):
|
|||||||
data['vertex_colors'][color_map.name]['data'] = np_dump_collection_primitive(color_map.data, 'color')
|
data['vertex_colors'][color_map.name]['data'] = np_dump_collection_primitive(color_map.data, 'color')
|
||||||
|
|
||||||
# Fix material index
|
# Fix material index
|
||||||
data['material_list'] = [(m.uuid, m.name) for m in instance.materials if m]
|
m_list = []
|
||||||
|
for material in instance.materials:
|
||||||
|
if material:
|
||||||
|
m_list.append(material.name)
|
||||||
|
|
||||||
|
data['material_list'] = m_list
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
# ##### 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)
|
|
@ -24,6 +24,7 @@ from replication.exception import ContextError
|
|||||||
|
|
||||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
||||||
from .dump_anything import Dumper, Loader
|
from .dump_anything import Dumper, Loader
|
||||||
|
from replication.exception import ReparentException
|
||||||
|
|
||||||
|
|
||||||
def load_pose(target_bone, data):
|
def load_pose(target_bone, data):
|
||||||
@ -119,7 +120,9 @@ class BlObject(BlDatablock):
|
|||||||
data_uuid = data.get("data_uuid")
|
data_uuid = data.get("data_uuid")
|
||||||
data_id = data.get("data")
|
data_id = data.get("data")
|
||||||
|
|
||||||
if target.data and (target.data.name != data_id):
|
if target.type != data['type']:
|
||||||
|
raise ReparentException()
|
||||||
|
elif target.data and (target.data.name != data_id):
|
||||||
target.data = get_datablock_from_uuid(data_uuid, find_data_from_name(data_id), ignore=['images'])
|
target.data = get_datablock_from_uuid(data_uuid, find_data_from_name(data_id), ignore=['images'])
|
||||||
|
|
||||||
# vertex groups
|
# vertex groups
|
||||||
@ -188,10 +191,10 @@ class BlObject(BlDatablock):
|
|||||||
target_bone.bone_group = target.pose.bone_group[bone_data['bone_group_index']]
|
target_bone.bone_group = target.pose.bone_group[bone_data['bone_group_index']]
|
||||||
|
|
||||||
# TODO: find another way...
|
# TODO: find another way...
|
||||||
if target.empty_display_type == "IMAGE":
|
if target.type == 'EMPTY':
|
||||||
img_uuid = data.get('data_uuid')
|
img_uuid = data.get('data_uuid')
|
||||||
if target.data is None and img_uuid:
|
if target.data is None and img_uuid:
|
||||||
target.data = get_datablock_from_uuid(img_uuid, None)
|
target.data = get_datablock_from_uuid(img_uuid, None)#bpy.data.images.get(img_key, None)
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
assert(instance)
|
assert(instance)
|
||||||
|
@ -16,18 +16,15 @@
|
|||||||
# ##### END GPL LICENSE BLOCK #####
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
from deepdiff import DeepDiff
|
|
||||||
from replication.constants import DIFF_JSON, MODIFIED
|
|
||||||
|
|
||||||
from .bl_collection import (dump_collection_children, dump_collection_objects,
|
from .dump_anything import Loader, Dumper
|
||||||
load_collection_childrens, load_collection_objects,
|
|
||||||
resolve_collection_dependencies)
|
|
||||||
from .bl_datablock import BlDatablock
|
from .bl_datablock import BlDatablock
|
||||||
from .dump_anything import Dumper, Loader
|
from .bl_collection import dump_collection_children, dump_collection_objects, load_collection_childrens, load_collection_objects
|
||||||
|
from replication.constants import (DIFF_JSON, MODIFIED)
|
||||||
|
from deepdiff import DeepDiff
|
||||||
|
import logging
|
||||||
|
|
||||||
RENDER_SETTINGS = [
|
RENDER_SETTINGS = [
|
||||||
'dither_intensity',
|
'dither_intensity',
|
||||||
@ -264,12 +261,6 @@ VIEW_SETTINGS = [
|
|||||||
'black_level'
|
'black_level'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BlScene(BlDatablock):
|
class BlScene(BlDatablock):
|
||||||
bl_id = "scenes"
|
bl_id = "scenes"
|
||||||
bl_class = bpy.types.Scene
|
bl_class = bpy.types.Scene
|
||||||
@ -319,7 +310,7 @@ class BlScene(BlDatablock):
|
|||||||
if 'view_settings' in data.keys():
|
if 'view_settings' in data.keys():
|
||||||
loader.load(target.view_settings, data['view_settings'])
|
loader.load(target.view_settings, data['view_settings'])
|
||||||
if target.view_settings.use_curve_mapping and \
|
if target.view_settings.use_curve_mapping and \
|
||||||
'curve_mapping' in data['view_settings']:
|
'curve_mapping' in data['view_settings']:
|
||||||
# TODO: change this ugly fix
|
# TODO: change this ugly fix
|
||||||
target.view_settings.curve_mapping.white_level = data[
|
target.view_settings.curve_mapping.white_level = data[
|
||||||
'view_settings']['curve_mapping']['white_level']
|
'view_settings']['curve_mapping']['white_level']
|
||||||
@ -329,8 +320,8 @@ class BlScene(BlDatablock):
|
|||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
assert(instance)
|
assert(instance)
|
||||||
|
data = {}
|
||||||
|
|
||||||
# Metadata
|
|
||||||
scene_dumper = Dumper()
|
scene_dumper = Dumper()
|
||||||
scene_dumper.depth = 1
|
scene_dumper.depth = 1
|
||||||
scene_dumper.include_filter = [
|
scene_dumper.include_filter = [
|
||||||
@ -345,9 +336,11 @@ class BlScene(BlDatablock):
|
|||||||
if self.preferences.sync_flags.sync_active_camera:
|
if self.preferences.sync_flags.sync_active_camera:
|
||||||
scene_dumper.include_filter.append('camera')
|
scene_dumper.include_filter.append('camera')
|
||||||
|
|
||||||
data.update(scene_dumper.dump(instance))
|
data = scene_dumper.dump(instance)
|
||||||
|
|
||||||
# Master collection
|
scene_dumper.depth = 3
|
||||||
|
|
||||||
|
scene_dumper.include_filter = ['children', 'objects', 'name']
|
||||||
data['collection'] = {}
|
data['collection'] = {}
|
||||||
data['collection']['children'] = dump_collection_children(
|
data['collection']['children'] = dump_collection_children(
|
||||||
instance.collection)
|
instance.collection)
|
||||||
@ -357,7 +350,6 @@ class BlScene(BlDatablock):
|
|||||||
scene_dumper.depth = 1
|
scene_dumper.depth = 1
|
||||||
scene_dumper.include_filter = None
|
scene_dumper.include_filter = None
|
||||||
|
|
||||||
# Render settings
|
|
||||||
if self.preferences.sync_flags.sync_render_settings:
|
if self.preferences.sync_flags.sync_render_settings:
|
||||||
scene_dumper.include_filter = RENDER_SETTINGS
|
scene_dumper.include_filter = RENDER_SETTINGS
|
||||||
|
|
||||||
@ -385,18 +377,18 @@ class BlScene(BlDatablock):
|
|||||||
data['view_settings']['curve_mapping']['curves'] = scene_dumper.dump(
|
data['view_settings']['curve_mapping']['curves'] = scene_dumper.dump(
|
||||||
instance.view_settings.curve_mapping.curves)
|
instance.view_settings.curve_mapping.curves)
|
||||||
|
|
||||||
if instance.sequence_editor:
|
|
||||||
data['has_sequence'] = True
|
|
||||||
else:
|
|
||||||
data['has_sequence'] = False
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def _resolve_deps_implementation(self):
|
def _resolve_deps_implementation(self):
|
||||||
deps = []
|
deps = []
|
||||||
|
|
||||||
# Master Collection
|
# child collections
|
||||||
deps.extend(resolve_collection_dependencies(self.instance.collection))
|
for child in self.instance.collection.children:
|
||||||
|
deps.append(child)
|
||||||
|
|
||||||
|
# childs objects
|
||||||
|
for object in self.instance.collection.objects:
|
||||||
|
deps.append(object)
|
||||||
|
|
||||||
# world
|
# world
|
||||||
if self.instance.world:
|
if self.instance.world:
|
||||||
@ -406,11 +398,6 @@ class BlScene(BlDatablock):
|
|||||||
if self.instance.grease_pencil:
|
if self.instance.grease_pencil:
|
||||||
deps.append(self.instance.grease_pencil)
|
deps.append(self.instance.grease_pencil)
|
||||||
|
|
||||||
# Sequences
|
|
||||||
# deps.extend(list(self.instance.sequence_editor.sequences_all))
|
|
||||||
if self.instance.sequence_editor:
|
|
||||||
deps.append(self.instance.sequence_editor)
|
|
||||||
|
|
||||||
return deps
|
return deps
|
||||||
|
|
||||||
def diff(self):
|
def diff(self):
|
||||||
|
@ -1,197 +0,0 @@
|
|||||||
# ##### 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 pathlib import Path
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from .bl_file import get_filepath
|
|
||||||
from .dump_anything import Loader, Dumper
|
|
||||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
|
||||||
|
|
||||||
def dump_sequence(sequence: bpy.types.Sequence) -> dict:
|
|
||||||
""" Dump a sequence to a dict
|
|
||||||
|
|
||||||
:arg sequence: sequence to dump
|
|
||||||
:type sequence: bpy.types.Sequence
|
|
||||||
:return dict:
|
|
||||||
"""
|
|
||||||
dumper = Dumper()
|
|
||||||
dumper.exclude_filter = [
|
|
||||||
'lock',
|
|
||||||
'select',
|
|
||||||
'select_left_handle',
|
|
||||||
'select_right_handle',
|
|
||||||
'strobe'
|
|
||||||
]
|
|
||||||
dumper.depth = 1
|
|
||||||
data = dumper.dump(sequence)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Support multiple images
|
|
||||||
if sequence.type == 'IMAGE':
|
|
||||||
data['filenames'] = [e.filename for e in sequence.elements]
|
|
||||||
|
|
||||||
|
|
||||||
# Effect strip inputs
|
|
||||||
input_count = getattr(sequence, 'input_count', None)
|
|
||||||
if input_count:
|
|
||||||
for n in range(input_count):
|
|
||||||
input_name = f"input_{n+1}"
|
|
||||||
data[input_name] = getattr(sequence, input_name).name
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor):
|
|
||||||
""" Load sequence from dumped data
|
|
||||||
|
|
||||||
:arg sequence_data: sequence to dump
|
|
||||||
:type sequence_data:dict
|
|
||||||
:arg sequence_editor: root sequence editor
|
|
||||||
:type sequence_editor: bpy.types.SequenceEditor
|
|
||||||
"""
|
|
||||||
strip_type = sequence_data.get('type')
|
|
||||||
strip_name = sequence_data.get('name')
|
|
||||||
strip_channel = sequence_data.get('channel')
|
|
||||||
strip_frame_start = sequence_data.get('frame_start')
|
|
||||||
|
|
||||||
sequence = sequence_editor.sequences_all.get(strip_name, None)
|
|
||||||
|
|
||||||
if sequence is None:
|
|
||||||
if strip_type == 'SCENE':
|
|
||||||
strip_scene = bpy.data.scenes.get(sequence_data.get('scene'))
|
|
||||||
sequence = sequence_editor.sequences.new_scene(strip_name,
|
|
||||||
strip_scene,
|
|
||||||
strip_channel,
|
|
||||||
strip_frame_start)
|
|
||||||
elif strip_type == 'MOVIE':
|
|
||||||
filepath = get_filepath(Path(sequence_data['filepath']).name)
|
|
||||||
sequence = sequence_editor.sequences.new_movie(strip_name,
|
|
||||||
filepath,
|
|
||||||
strip_channel,
|
|
||||||
strip_frame_start)
|
|
||||||
elif strip_type == 'SOUND':
|
|
||||||
filepath = bpy.data.sounds[sequence_data['sound']].filepath
|
|
||||||
sequence = sequence_editor.sequences.new_sound(strip_name,
|
|
||||||
filepath,
|
|
||||||
strip_channel,
|
|
||||||
strip_frame_start)
|
|
||||||
elif strip_type == 'IMAGE':
|
|
||||||
images_name = sequence_data.get('filenames')
|
|
||||||
filepath = get_filepath(images_name[0])
|
|
||||||
sequence = sequence_editor.sequences.new_image(strip_name,
|
|
||||||
filepath,
|
|
||||||
strip_channel,
|
|
||||||
strip_frame_start)
|
|
||||||
# load other images
|
|
||||||
if len(images_name)>1:
|
|
||||||
for img_idx in range(1,len(images_name)):
|
|
||||||
sequence.elements.append((images_name[img_idx]))
|
|
||||||
else:
|
|
||||||
seq = {}
|
|
||||||
|
|
||||||
for i in range(sequence_data['input_count']):
|
|
||||||
seq[f"seq{i+1}"] = sequence_editor.sequences_all.get(sequence_data.get(f"input_{i+1}", None))
|
|
||||||
|
|
||||||
sequence = sequence_editor.sequences.new_effect(name=strip_name,
|
|
||||||
type=strip_type,
|
|
||||||
channel=strip_channel,
|
|
||||||
frame_start=strip_frame_start,
|
|
||||||
frame_end=sequence_data['frame_final_end'],
|
|
||||||
**seq)
|
|
||||||
|
|
||||||
loader = Loader()
|
|
||||||
loader.load(sequence, sequence_data)
|
|
||||||
sequence.select = False
|
|
||||||
|
|
||||||
|
|
||||||
class BlSequencer(BlDatablock):
|
|
||||||
bl_id = "scenes"
|
|
||||||
bl_class = bpy.types.SequenceEditor
|
|
||||||
bl_delay_refresh = 1
|
|
||||||
bl_delay_apply = 1
|
|
||||||
bl_automatic_push = True
|
|
||||||
bl_check_common = True
|
|
||||||
bl_icon = 'SEQUENCE'
|
|
||||||
|
|
||||||
def _construct(self, data):
|
|
||||||
# Get the scene
|
|
||||||
scene_id = data.get('name')
|
|
||||||
scene = bpy.data.scenes.get(scene_id, None)
|
|
||||||
|
|
||||||
# Create sequencer data
|
|
||||||
scene.sequence_editor_clear()
|
|
||||||
scene.sequence_editor_create()
|
|
||||||
|
|
||||||
return scene.sequence_editor
|
|
||||||
|
|
||||||
def resolve(self):
|
|
||||||
scene = bpy.data.scenes.get(self.data['name'], None)
|
|
||||||
if scene:
|
|
||||||
if scene.sequence_editor is None:
|
|
||||||
self.instance = self._construct(self.data)
|
|
||||||
else:
|
|
||||||
self.instance = scene.sequence_editor
|
|
||||||
else:
|
|
||||||
logging.warning("Sequencer editor scene not found")
|
|
||||||
|
|
||||||
def _load_implementation(self, data, target):
|
|
||||||
loader = Loader()
|
|
||||||
# Sequencer
|
|
||||||
sequences = data.get('sequences')
|
|
||||||
if sequences:
|
|
||||||
for seq in target.sequences_all:
|
|
||||||
if seq.name not in sequences:
|
|
||||||
target.sequences.remove(seq)
|
|
||||||
for seq_name, seq_data in sequences.items():
|
|
||||||
load_sequence(seq_data, target)
|
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
|
||||||
assert(instance)
|
|
||||||
sequence_dumper = Dumper()
|
|
||||||
sequence_dumper.depth = 1
|
|
||||||
sequence_dumper.include_filter = [
|
|
||||||
'proxy_storage',
|
|
||||||
]
|
|
||||||
data = {}#sequence_dumper.dump(instance)
|
|
||||||
# Sequencer
|
|
||||||
sequences = {}
|
|
||||||
|
|
||||||
for seq in instance.sequences_all:
|
|
||||||
sequences[seq.name] = dump_sequence(seq)
|
|
||||||
|
|
||||||
data['sequences'] = sequences
|
|
||||||
data['name'] = instance.id_data.name
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_deps_implementation(self):
|
|
||||||
deps = []
|
|
||||||
|
|
||||||
for seq in self.instance.sequences_all:
|
|
||||||
if seq.type == 'MOVIE' and seq.filepath:
|
|
||||||
deps.append(Path(bpy.path.abspath(seq.filepath)))
|
|
||||||
elif seq.type == 'SOUND' and seq.sound:
|
|
||||||
deps.append(seq.sound)
|
|
||||||
elif seq.type == 'IMAGE':
|
|
||||||
for e in seq.elements:
|
|
||||||
deps.append(Path(bpy.path.abspath(seq.directory), e.filename))
|
|
||||||
return deps
|
|
@ -21,8 +21,10 @@ import mathutils
|
|||||||
|
|
||||||
from .dump_anything import Loader, Dumper
|
from .dump_anything import Loader, Dumper
|
||||||
from .bl_datablock import BlDatablock
|
from .bl_datablock import BlDatablock
|
||||||
from .bl_material import (load_shader_node_tree,
|
from .bl_material import (load_links,
|
||||||
dump_shader_node_tree,
|
load_node,
|
||||||
|
dump_node,
|
||||||
|
dump_links,
|
||||||
get_node_tree_dependencies)
|
get_node_tree_dependencies)
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +48,15 @@ class BlWorld(BlDatablock):
|
|||||||
if target.node_tree is None:
|
if target.node_tree is None:
|
||||||
target.use_nodes = True
|
target.use_nodes = True
|
||||||
|
|
||||||
load_shader_node_tree(data['node_tree'], target.node_tree)
|
target.node_tree.nodes.clear()
|
||||||
|
|
||||||
|
for node in data["node_tree"]["nodes"]:
|
||||||
|
load_node(data["node_tree"]["nodes"][node], target.node_tree)
|
||||||
|
|
||||||
|
# Load nodes links
|
||||||
|
target.node_tree.links.clear()
|
||||||
|
|
||||||
|
load_links(data["node_tree"]["links"], target.node_tree)
|
||||||
|
|
||||||
def _dump_implementation(self, data, instance=None):
|
def _dump_implementation(self, data, instance=None):
|
||||||
assert(instance)
|
assert(instance)
|
||||||
@ -60,7 +70,15 @@ class BlWorld(BlDatablock):
|
|||||||
]
|
]
|
||||||
data = world_dumper.dump(instance)
|
data = world_dumper.dump(instance)
|
||||||
if instance.use_nodes:
|
if instance.use_nodes:
|
||||||
data['node_tree'] = dump_shader_node_tree(instance.node_tree)
|
data['node_tree'] = {}
|
||||||
|
nodes = {}
|
||||||
|
|
||||||
|
for node in instance.node_tree.nodes:
|
||||||
|
nodes[node.name] = dump_node(node)
|
||||||
|
|
||||||
|
data["node_tree"]['nodes'] = nodes
|
||||||
|
|
||||||
|
data["node_tree"]['links'] = dump_links(instance.node_tree.links)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@ from replication.constants import (FETCHED,
|
|||||||
STATE_ACTIVE,
|
STATE_ACTIVE,
|
||||||
STATE_SYNCING,
|
STATE_SYNCING,
|
||||||
STATE_LOBBY,
|
STATE_LOBBY,
|
||||||
STATE_SRV_SYNC)
|
STATE_SRV_SYNC,
|
||||||
|
REPARENT)
|
||||||
|
|
||||||
from replication.interface import session
|
from replication.interface import session
|
||||||
from replication.exception import NonAuthorizedOperationError
|
from replication.exception import NonAuthorizedOperationError
|
||||||
@ -121,6 +122,15 @@ class ApplyTimer(Timer):
|
|||||||
session.apply(node)
|
session.apply(node)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
||||||
|
elif node_ref.state == REPARENT:
|
||||||
|
# Reload the node
|
||||||
|
node_ref.remove_instance()
|
||||||
|
node_ref.resolve()
|
||||||
|
session.apply(node)
|
||||||
|
for parent in session._graph.find_parents(node):
|
||||||
|
logging.info(f"Applying parent {parent}")
|
||||||
|
session.apply(parent, force=True)
|
||||||
|
node_ref.state = UP
|
||||||
|
|
||||||
|
|
||||||
class DynamicRightSelectTimer(Timer):
|
class DynamicRightSelectTimer(Timer):
|
||||||
@ -161,8 +171,7 @@ class DynamicRightSelectTimer(Timer):
|
|||||||
session.change_owner(
|
session.change_owner(
|
||||||
node.uuid,
|
node.uuid,
|
||||||
RP_COMMON,
|
RP_COMMON,
|
||||||
ignore_warnings=True,
|
recursive=recursive)
|
||||||
affect_dependencies=recursive)
|
|
||||||
except NonAuthorizedOperationError:
|
except NonAuthorizedOperationError:
|
||||||
logging.warning(f"Not authorized to change {node} owner")
|
logging.warning(f"Not authorized to change {node} owner")
|
||||||
|
|
||||||
@ -179,8 +188,7 @@ class DynamicRightSelectTimer(Timer):
|
|||||||
session.change_owner(
|
session.change_owner(
|
||||||
node.uuid,
|
node.uuid,
|
||||||
settings.username,
|
settings.username,
|
||||||
ignore_warnings=True,
|
recursive=recursive)
|
||||||
affect_dependencies=recursive)
|
|
||||||
except NonAuthorizedOperationError:
|
except NonAuthorizedOperationError:
|
||||||
logging.warning(f"Not authorized to change {node} owner")
|
logging.warning(f"Not authorized to change {node} owner")
|
||||||
else:
|
else:
|
||||||
@ -205,8 +213,7 @@ class DynamicRightSelectTimer(Timer):
|
|||||||
session.change_owner(
|
session.change_owner(
|
||||||
key,
|
key,
|
||||||
RP_COMMON,
|
RP_COMMON,
|
||||||
ignore_warnings=True,
|
recursive=recursive)
|
||||||
affect_dependencies=recursive)
|
|
||||||
except NonAuthorizedOperationError:
|
except NonAuthorizedOperationError:
|
||||||
logging.warning(f"Not authorized to change {key} owner")
|
logging.warning(f"Not authorized to change {key} owner")
|
||||||
|
|
||||||
|
@ -62,9 +62,6 @@ def install_package(name, version):
|
|||||||
del env["PIP_REQUIRE_VIRTUALENV"]
|
del env["PIP_REQUIRE_VIRTUALENV"]
|
||||||
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", f"{name}=={version}"], env=env)
|
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", f"{name}=={version}"], env=env)
|
||||||
|
|
||||||
if name in sys.modules:
|
|
||||||
del sys.modules[name]
|
|
||||||
|
|
||||||
def check_package_version(name, required_version):
|
def check_package_version(name, required_version):
|
||||||
logging.info(f"Checking {name} version...")
|
logging.info(f"Checking {name} version...")
|
||||||
out = subprocess.run([str(PYTHON_PATH), "-m", "pip", "show", name], capture_output=True)
|
out = subprocess.run([str(PYTHON_PATH), "-m", "pip", "show", name], capture_output=True)
|
||||||
|
@ -166,8 +166,7 @@ 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)
|
||||||
name = [e.capitalize() for e in type.split('_')[1:]]
|
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
||||||
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)
|
||||||
@ -227,8 +226,7 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.report({'ERROR'}, repr(e))
|
self.report({'ERROR'}, repr(e))
|
||||||
logging.error(f"Error: {e}")
|
logging.error(f"Error: {e}")
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
# Join a session
|
# Join a session
|
||||||
else:
|
else:
|
||||||
if not runtime_settings.admin:
|
if not runtime_settings.admin:
|
||||||
@ -428,8 +426,7 @@ class SessionPropertyRightOperator(bpy.types.Operator):
|
|||||||
if session:
|
if session:
|
||||||
session.change_owner(self.key,
|
session.change_owner(self.key,
|
||||||
runtime_settings.clients,
|
runtime_settings.clients,
|
||||||
ignore_warnings=True,
|
recursive=self.recursive)
|
||||||
affect_dependencies=self.recursive)
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
@ -29,9 +29,8 @@ from .utils import get_preferences, get_expanded_icon
|
|||||||
from replication.constants import RP_COMMON
|
from replication.constants import RP_COMMON
|
||||||
from replication.interface import session
|
from replication.interface import session
|
||||||
|
|
||||||
# From https://stackoverflow.com/a/106223
|
IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+')
|
||||||
IP_REGEX = re.compile("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
|
|
||||||
HOSTNAME_REGEX = re.compile("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")
|
|
||||||
|
|
||||||
def randomColor():
|
def randomColor():
|
||||||
"""Generate a random color """
|
"""Generate a random color """
|
||||||
@ -54,13 +53,10 @@ def update_panel_category(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_ip(self, context):
|
def update_ip(self, context):
|
||||||
ip = IP_REGEX.search(self.ip)
|
ip = IP_EXPR.search(self.ip)
|
||||||
dns = HOSTNAME_REGEX.search(self.ip)
|
|
||||||
|
|
||||||
if ip:
|
if ip:
|
||||||
self['ip'] = ip.group()
|
self['ip'] = ip.group()
|
||||||
elif dns:
|
|
||||||
self['ip'] = dns.group()
|
|
||||||
else:
|
else:
|
||||||
logging.error("Wrong IP format")
|
logging.error("Wrong IP format")
|
||||||
self['ip'] = "127.0.0.1"
|
self['ip'] = "127.0.0.1"
|
||||||
@ -242,31 +238,6 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
set=set_log_level,
|
set=set_log_level,
|
||||||
get=get_log_level
|
get=get_log_level
|
||||||
)
|
)
|
||||||
presence_hud_scale: bpy.props.FloatProperty(
|
|
||||||
name="Text scale",
|
|
||||||
description="Adjust the session widget text scale",
|
|
||||||
min=7,
|
|
||||||
max=90,
|
|
||||||
default=25,
|
|
||||||
)
|
|
||||||
presence_hud_hpos: bpy.props.FloatProperty(
|
|
||||||
name="Horizontal position",
|
|
||||||
description="Adjust the session widget horizontal position",
|
|
||||||
min=1,
|
|
||||||
max=90,
|
|
||||||
default=1,
|
|
||||||
step=1,
|
|
||||||
subtype='PERCENTAGE',
|
|
||||||
)
|
|
||||||
presence_hud_vpos: bpy.props.FloatProperty(
|
|
||||||
name="Vertical position",
|
|
||||||
description="Adjust the session widget vertical position",
|
|
||||||
min=1,
|
|
||||||
max=94,
|
|
||||||
default=1,
|
|
||||||
step=1,
|
|
||||||
subtype='PERCENTAGE',
|
|
||||||
)
|
|
||||||
conf_session_identity_expanded: bpy.props.BoolProperty(
|
conf_session_identity_expanded: bpy.props.BoolProperty(
|
||||||
name="Identity",
|
name="Identity",
|
||||||
description="Identity",
|
description="Identity",
|
||||||
@ -441,15 +412,6 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
emboss=False)
|
emboss=False)
|
||||||
if self.conf_session_ui_expanded:
|
if self.conf_session_ui_expanded:
|
||||||
box.row().prop(self, "panel_category", text="Panel category", expand=True)
|
box.row().prop(self, "panel_category", text="Panel category", expand=True)
|
||||||
row = box.row()
|
|
||||||
row.label(text="Session widget:")
|
|
||||||
|
|
||||||
col = box.column(align=True)
|
|
||||||
col.prop(self, "presence_hud_scale", expand=True)
|
|
||||||
|
|
||||||
|
|
||||||
col.prop(self, "presence_hud_hpos", expand=True)
|
|
||||||
col.prop(self, "presence_hud_vpos", expand=True)
|
|
||||||
|
|
||||||
if self.category == 'UPDATE':
|
if self.category == 'UPDATE':
|
||||||
from . import addon_updater_ops
|
from . import addon_updater_ops
|
||||||
@ -462,9 +424,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)
|
||||||
name = [e.capitalize() for e in type.split('_')[1:]]
|
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
||||||
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
|
||||||
|
@ -35,7 +35,7 @@ from replication.constants import (STATE_ACTIVE, STATE_AUTH, STATE_CONFIG,
|
|||||||
STATE_SYNCING, STATE_WAITING)
|
STATE_SYNCING, STATE_WAITING)
|
||||||
from replication.interface import session
|
from replication.interface import session
|
||||||
|
|
||||||
from .utils import find_from_attr, get_state_str, get_preferences
|
from .utils import find_from_attr, get_state_str
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
@ -300,38 +300,41 @@ class UserSelectionWidget(Widget):
|
|||||||
ob = find_from_attr("uuid", select_ob, bpy.data.objects)
|
ob = find_from_attr("uuid", select_ob, bpy.data.objects)
|
||||||
if not ob:
|
if not ob:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
position = None
|
||||||
|
|
||||||
vertex_pos = bbox_from_obj(ob, 1.0)
|
if ob.type == 'EMPTY':
|
||||||
vertex_indices = ((0, 1), (0, 2), (1, 3), (2, 3),
|
# TODO: Child case
|
||||||
(4, 5), (4, 6), (5, 7), (6, 7),
|
# Collection instance case
|
||||||
(0, 4), (1, 5), (2, 6), (3, 7))
|
indices = (
|
||||||
|
|
||||||
if ob.instance_collection:
|
|
||||||
for obj in ob.instance_collection.objects:
|
|
||||||
if obj.type == 'MESH' and hasattr(obj, 'bound_box'):
|
|
||||||
vertex_pos = get_bb_coords_from_obj(obj, instance=ob)
|
|
||||||
break
|
|
||||||
elif ob.type == 'EMPTY':
|
|
||||||
vertex_pos = bbox_from_obj(ob, ob.empty_display_size)
|
|
||||||
elif ob.type == 'LIGHT':
|
|
||||||
vertex_pos = bbox_from_obj(ob, ob.data.shadow_soft_size)
|
|
||||||
elif ob.type == 'LIGHT_PROBE':
|
|
||||||
vertex_pos = bbox_from_obj(ob, ob.data.influence_distance)
|
|
||||||
elif ob.type == 'CAMERA':
|
|
||||||
vertex_pos = bbox_from_obj(ob, ob.data.display_size)
|
|
||||||
elif hasattr(ob, 'bound_box'):
|
|
||||||
vertex_indices = (
|
|
||||||
(0, 1), (1, 2), (2, 3), (0, 3),
|
(0, 1), (1, 2), (2, 3), (0, 3),
|
||||||
(4, 5), (5, 6), (6, 7), (4, 7),
|
(4, 5), (5, 6), (6, 7), (4, 7),
|
||||||
(0, 4), (1, 5), (2, 6), (3, 7))
|
(0, 4), (1, 5), (2, 6), (3, 7))
|
||||||
vertex_pos = get_bb_coords_from_obj(ob)
|
if ob.instance_collection:
|
||||||
|
for obj in ob.instance_collection.objects:
|
||||||
|
if obj.type == 'MESH' and hasattr(obj, 'bound_box'):
|
||||||
|
positions = get_bb_coords_from_obj(obj, instance=ob)
|
||||||
|
break
|
||||||
|
elif hasattr(ob, 'bound_box'):
|
||||||
|
indices = (
|
||||||
|
(0, 1), (1, 2), (2, 3), (0, 3),
|
||||||
|
(4, 5), (5, 6), (6, 7), (4, 7),
|
||||||
|
(0, 4), (1, 5), (2, 6), (3, 7))
|
||||||
|
positions = get_bb_coords_from_obj(ob)
|
||||||
|
if positions is None:
|
||||||
|
indices = (
|
||||||
|
(0, 1), (0, 2), (1, 3), (2, 3),
|
||||||
|
(4, 5), (4, 6), (5, 7), (6, 7),
|
||||||
|
(0, 4), (1, 5), (2, 6), (3, 7))
|
||||||
|
|
||||||
|
positions = bbox_from_obj(ob, ob.scale.x)
|
||||||
|
|
||||||
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
|
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
|
||||||
batch = batch_for_shader(
|
batch = batch_for_shader(
|
||||||
shader,
|
shader,
|
||||||
'LINES',
|
'LINES',
|
||||||
{"pos": vertex_pos},
|
{"pos": positions},
|
||||||
indices=vertex_indices)
|
indices=indices)
|
||||||
|
|
||||||
shader.bind()
|
shader.bind()
|
||||||
shader.uniform_float("color", self.data.get('color'))
|
shader.uniform_float("color", self.data.get('color'))
|
||||||
@ -384,9 +387,6 @@ class UserNameWidget(Widget):
|
|||||||
class SessionStatusWidget(Widget):
|
class SessionStatusWidget(Widget):
|
||||||
draw_type = 'POST_PIXEL'
|
draw_type = 'POST_PIXEL'
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.preferences = get_preferences()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def settings(self):
|
def settings(self):
|
||||||
return getattr(bpy.context.window_manager, 'session', None)
|
return getattr(bpy.context.window_manager, 'session', None)
|
||||||
@ -396,8 +396,6 @@ class SessionStatusWidget(Widget):
|
|||||||
self.settings.enable_presence
|
self.settings.enable_presence
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
text_scale = self.preferences.presence_hud_scale
|
|
||||||
ui_scale = bpy.context.preferences.view.ui_scale
|
|
||||||
color = [1, 1, 0, 1]
|
color = [1, 1, 0, 1]
|
||||||
state = session.state.get('STATE')
|
state = session.state.get('STATE')
|
||||||
state_str = f"{get_state_str(state)}"
|
state_str = f"{get_state_str(state)}"
|
||||||
@ -406,11 +404,9 @@ class SessionStatusWidget(Widget):
|
|||||||
color = [0, 1, 0, 1]
|
color = [0, 1, 0, 1]
|
||||||
elif state == STATE_INITIAL:
|
elif state == STATE_INITIAL:
|
||||||
color = [1, 0, 0, 1]
|
color = [1, 0, 0, 1]
|
||||||
hpos = (self.preferences.presence_hud_hpos*bpy.context.area.width)/100
|
|
||||||
vpos = (self.preferences.presence_hud_vpos*bpy.context.area.height)/100
|
|
||||||
|
|
||||||
blf.position(0, hpos, vpos, 0)
|
blf.position(0, 10, 20, 0)
|
||||||
blf.size(0, int(text_scale*ui_scale), 72)
|
blf.size(0, 16, 45)
|
||||||
blf.color(0, color[0], color[1], color[2], color[3])
|
blf.color(0, color[0], color[1], color[2], color[3])
|
||||||
blf.draw(0, state_str)
|
blf.draw(0, state_str)
|
||||||
|
|
||||||
|
@ -448,17 +448,9 @@ class SESSION_PT_presence(bpy.types.Panel):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
settings = context.window_manager.session
|
settings = context.window_manager.session
|
||||||
pref = get_preferences()
|
|
||||||
layout.active = settings.enable_presence
|
layout.active = settings.enable_presence
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(settings, "presence_show_session_status")
|
col.prop(settings, "presence_show_session_status")
|
||||||
row = col.column()
|
|
||||||
row.active = settings.presence_show_session_status
|
|
||||||
row.prop(pref, "presence_hud_scale", expand=True)
|
|
||||||
row = col.column(align=True)
|
|
||||||
row.active = settings.presence_show_session_status
|
|
||||||
row.prop(pref, "presence_hud_hpos", expand=True)
|
|
||||||
row.prop(pref, "presence_hud_vpos", expand=True)
|
|
||||||
col.prop(settings, "presence_show_selected")
|
col.prop(settings, "presence_show_selected")
|
||||||
col.prop(settings, "presence_show_user")
|
col.prop(settings, "presence_show_user")
|
||||||
row = layout.column()
|
row = layout.column()
|
||||||
@ -630,7 +622,7 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
|
|||||||
col.prop(settings, "presence_show_session_status")
|
col.prop(settings, "presence_show_session_status")
|
||||||
col.prop(settings, "presence_show_selected")
|
col.prop(settings, "presence_show_selected")
|
||||||
col.prop(settings, "presence_show_user")
|
col.prop(settings, "presence_show_user")
|
||||||
|
|
||||||
row = layout.column()
|
row = layout.column()
|
||||||
row.active = settings.presence_show_user
|
row.active = settings.presence_show_user
|
||||||
row.prop(settings, "presence_show_far_user")
|
row.prop(settings, "presence_show_far_user")
|
||||||
|
@ -99,9 +99,7 @@ def clean_scene():
|
|||||||
type_collection.remove(item)
|
type_collection.remove(item)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Clear sequencer
|
|
||||||
bpy.context.scene.sequence_editor_clear()
|
|
||||||
|
|
||||||
def get_selected_objects(scene, active_view_layer):
|
def get_selected_objects(scene, active_view_layer):
|
||||||
return [obj.uuid for obj in scene.objects if obj.select_get(view_layer=active_view_layer)]
|
return [obj.uuid for obj in scene.objects if obj.select_get(view_layer=active_view_layer)]
|
||||||
|
Loading…
Reference in New Issue
Block a user