Revert "Merge branch '45-vse-support' into 'master'"

This reverts merge request !68
This commit is contained in:
Swann Martinez 2020-11-11 19:31:06 +00:00
parent d42ed789e7
commit 761fa26ed7
24 changed files with 183 additions and 600 deletions

View File

@ -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

View File

@ -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

View File

@ -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'),
} }

View File

@ -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 *

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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"}

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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)]