Merge branch 'develop' into feature/event_driven_updates
This commit is contained in:
38
CHANGELOG.md
Normal file
38
CHANGELOG.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.0.2] - 2020-02-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Blender animation features support (alpha).
|
||||||
|
- Action.
|
||||||
|
- Armature (Unstable).
|
||||||
|
- Shape key.
|
||||||
|
- Drivers.
|
||||||
|
- Constraints.
|
||||||
|
- Snap to user timeline tool.
|
||||||
|
- Light probes support (only since 2.83).
|
||||||
|
- Metaballs support.
|
||||||
|
- Improved modifiers support.
|
||||||
|
- Online documentation.
|
||||||
|
- Improved Undo handling.
|
||||||
|
- Improved overall session handling:
|
||||||
|
- Time To Leave : ensure clients/server disconnect automatically on connection lost.
|
||||||
|
- Ping: show clients latency.
|
||||||
|
- Non-blocking connection.
|
||||||
|
- Connection state tracking.
|
||||||
|
- Service communication layer to manage background daemons.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- UI revamp:
|
||||||
|
- Show users frame.
|
||||||
|
- Expose IPC(inter process communication) port.
|
||||||
|
- New user list.
|
||||||
|
- Progress bar to track connection status.
|
||||||
|
- Right management takes view-layer in account for object selection.
|
||||||
|
- Use a basic BFS approach for replication graph pre-load.
|
||||||
|
- Serialization is now based on marshal (2x performance improvements).
|
||||||
|
- Let pip chose python dependencies install path.
|
@ -22,7 +22,7 @@ copyright = '2020, Swann Martinez'
|
|||||||
author = 'Swann Martinez'
|
author = 'Swann Martinez'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = '0.0.1'
|
release = '0.0.2'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
@ -4,6 +4,6 @@ Installation
|
|||||||
|
|
||||||
*The process is the same for linux, mac and windows.*
|
*The process is the same for linux, mac and windows.*
|
||||||
|
|
||||||
1. Download latest release `multi_user.zip <https://gitlab.com/slumber/multi-user/uploads/8aef79c7cf5b1d9606dc58307fd9ad8b/multi_user.zip>`_.
|
1. Download latest release `multi_user.zip <https://gitlab.com/slumber/multi-user/uploads/7ce1fd015f50f610e7deefda862d55b1/multi-user.zip>`_.
|
||||||
2. Run blender as administrator (to allow python dependencies auto-installation).
|
2. Run blender as administrator (to allow python dependencies auto-installation).
|
||||||
3. Install last_version.zip from your addon preferences.
|
3. Install last_version.zip from your addon preferences.
|
@ -1,11 +1,15 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Multi-User",
|
"name": "Multi-User",
|
||||||
"author": "Swann Martinez",
|
"author": "Swann Martinez",
|
||||||
"description": "",
|
"version": (0, 0, 2),
|
||||||
|
"description": "Enable real-time collaborative workflow inside blender",
|
||||||
"blender": (2, 80, 0),
|
"blender": (2, 80, 0),
|
||||||
"location": "",
|
"location": "3D View > Sidebar > Multi-User tab",
|
||||||
"warning": "Unstable addon, use it at your own risks",
|
"warning": "Unstable addon, use it at your own risks",
|
||||||
"category": "Collaboration"
|
"category": "Collaboration",
|
||||||
|
"wiki_url": "https://multi-user.readthedocs.io/en/develop/index.html",
|
||||||
|
"tracker_url": "https://gitlab.com/slumber/multi-user/issues",
|
||||||
|
"support": "COMMUNITY"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
|
import copy
|
||||||
|
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from .bl_datablock import BlDatablock
|
from .bl_datablock import BlDatablock
|
||||||
@ -54,18 +55,27 @@ class BlAction(BlDatablock):
|
|||||||
|
|
||||||
# paste dumped keyframes
|
# paste dumped keyframes
|
||||||
for dumped_keyframe_point in dumped_fcurve["keyframe_points"]:
|
for dumped_keyframe_point in dumped_fcurve["keyframe_points"]:
|
||||||
|
if dumped_keyframe_point['type'] == '':
|
||||||
|
dumped_keyframe_point['type'] = 'KEYFRAME'
|
||||||
|
|
||||||
new_kf = fcurve.keyframe_points.insert(
|
new_kf = fcurve.keyframe_points.insert(
|
||||||
dumped_keyframe_point["co"][0] - begin_frame,
|
dumped_keyframe_point["co"][0] - begin_frame,
|
||||||
dumped_keyframe_point["co"][1],
|
dumped_keyframe_point["co"][1],
|
||||||
options={'FAST', 'REPLACE'}
|
options={'FAST', 'REPLACE'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
keycache = copy.copy(dumped_keyframe_point)
|
||||||
|
keycache = utils.dump_anything.remove_items_from_dict(
|
||||||
|
keycache,
|
||||||
|
["co", "handle_left", "handle_right",'type']
|
||||||
|
)
|
||||||
|
|
||||||
loader.load(
|
loader.load(
|
||||||
new_kf,
|
new_kf,
|
||||||
utils.dump_anything.remove_items_from_dict(
|
keycache
|
||||||
dumped_keyframe_point,
|
|
||||||
["co", "handle_left", "handle_right"]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new_kf.type = dumped_keyframe_point['type']
|
||||||
new_kf.handle_left = [
|
new_kf.handle_left = [
|
||||||
dumped_keyframe_point["handle_left"][0] - begin_frame,
|
dumped_keyframe_point["handle_left"][0] - begin_frame,
|
||||||
dumped_keyframe_point["handle_left"][1]
|
dumped_keyframe_point["handle_left"][1]
|
||||||
@ -78,27 +88,29 @@ class BlAction(BlDatablock):
|
|||||||
# clearing (needed for blender to update well)
|
# clearing (needed for blender to update well)
|
||||||
if len(fcurve.keyframe_points) == 0:
|
if len(fcurve.keyframe_points) == 0:
|
||||||
target.fcurves.remove(fcurve)
|
target.fcurves.remove(fcurve)
|
||||||
|
target.id_root= data['id_root']
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump(self, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
data = utils.dump_datablock(pointer, 1)
|
|
||||||
|
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
dumper.depth = 2
|
|
||||||
dumper.exclude_filter =[
|
dumper.exclude_filter =[
|
||||||
'name_full',
|
'name_full',
|
||||||
'original',
|
'original',
|
||||||
'use_fake_user',
|
'use_fake_user',
|
||||||
'user',
|
'user',
|
||||||
'is_library_indirect',
|
'is_library_indirect',
|
||||||
'id_root',
|
|
||||||
'select_control_point',
|
'select_control_point',
|
||||||
'select_right_handle',
|
'select_right_handle',
|
||||||
'select_left_handle',
|
'select_left_handle',
|
||||||
'uuid'
|
'uuid',
|
||||||
|
'users'
|
||||||
]
|
]
|
||||||
|
dumper.depth = 1
|
||||||
|
data = dumper.dump(pointer)
|
||||||
|
|
||||||
|
|
||||||
data["fcurves"] = []
|
data["fcurves"] = []
|
||||||
|
dumper.depth = 2
|
||||||
for fcurve in self.pointer.fcurves:
|
for fcurve in self.pointer.fcurves:
|
||||||
fc = {
|
fc = {
|
||||||
"data_path": fcurve.data_path,
|
"data_path": fcurve.data_path,
|
||||||
|
@ -13,7 +13,7 @@ def load_gpencil_layer(target=None, data=None, create=False):
|
|||||||
|
|
||||||
for frame in data["frames"]:
|
for frame in data["frames"]:
|
||||||
|
|
||||||
tframe = target.frames.new(frame)
|
tframe = target.frames.new(data["frames"][frame]['frame_number'])
|
||||||
|
|
||||||
# utils.dump_anything.load(tframe, data["frames"][frame])
|
# utils.dump_anything.load(tframe, data["frames"][frame])
|
||||||
for stroke in data["frames"][frame]["strokes"]:
|
for stroke in data["frames"][frame]["strokes"]:
|
||||||
|
@ -78,7 +78,7 @@ class BlMaterial(BlDatablock):
|
|||||||
def construct(self, data):
|
def construct(self, data):
|
||||||
return bpy.data.materials.new(data["name"])
|
return bpy.data.materials.new(data["name"])
|
||||||
|
|
||||||
def load(self, data, target):
|
def load_implementation(self, data, target):
|
||||||
target.name = data['name']
|
target.name = data['name']
|
||||||
if data['is_grease_pencil']:
|
if data['is_grease_pencil']:
|
||||||
if not target.is_grease_pencil:
|
if not target.is_grease_pencil:
|
||||||
@ -95,6 +95,8 @@ class BlMaterial(BlDatablock):
|
|||||||
|
|
||||||
target.node_tree.nodes.clear()
|
target.node_tree.nodes.clear()
|
||||||
|
|
||||||
|
utils.dump_anything.load(target,data)
|
||||||
|
|
||||||
# Load nodes
|
# Load nodes
|
||||||
for node in data["node_tree"]["nodes"]:
|
for node in data["node_tree"]["nodes"]:
|
||||||
load_node(target.node_tree, data["node_tree"]["nodes"][node])
|
load_node(target.node_tree, data["node_tree"]["nodes"][node])
|
||||||
@ -122,6 +124,7 @@ class BlMaterial(BlDatablock):
|
|||||||
node_dumper.depth = 1
|
node_dumper.depth = 1
|
||||||
node_dumper.exclude_filter = [
|
node_dumper.exclude_filter = [
|
||||||
"dimensions",
|
"dimensions",
|
||||||
|
"show_expanded"
|
||||||
"select",
|
"select",
|
||||||
"bl_height_min",
|
"bl_height_min",
|
||||||
"bl_height_max",
|
"bl_height_max",
|
||||||
@ -140,7 +143,12 @@ class BlMaterial(BlDatablock):
|
|||||||
input_dumper.include_filter = ["default_value"]
|
input_dumper.include_filter = ["default_value"]
|
||||||
links_dumper = utils.dump_anything.Dumper()
|
links_dumper = utils.dump_anything.Dumper()
|
||||||
links_dumper.depth = 3
|
links_dumper.depth = 3
|
||||||
links_dumper.exclude_filter = ["dimensions"]
|
links_dumper.include_filter = [
|
||||||
|
"name",
|
||||||
|
"to_node",
|
||||||
|
"from_node",
|
||||||
|
"from_socket",
|
||||||
|
"to_socket"]
|
||||||
data = mat_dumper.dump(pointer)
|
data = mat_dumper.dump(pointer)
|
||||||
|
|
||||||
if pointer.use_nodes:
|
if pointer.use_nodes:
|
||||||
|
@ -114,6 +114,8 @@ class BlMesh(BlDatablock):
|
|||||||
v2 = data["edges"][i]["verts"][1]
|
v2 = data["edges"][i]["verts"][1]
|
||||||
edge = mesh_buffer.edges.new([verts[v1], verts[v2]])
|
edge = mesh_buffer.edges.new([verts[v1], verts[v2]])
|
||||||
edge.smooth = data["edges"][i]["smooth"]
|
edge.smooth = data["edges"][i]["smooth"]
|
||||||
|
|
||||||
|
mesh_buffer.edges.ensure_lookup_table()
|
||||||
for p in data["faces"]:
|
for p in data["faces"]:
|
||||||
verts = []
|
verts = []
|
||||||
for v in data["faces"][p]["verts"]:
|
for v in data["faces"][p]["verts"]:
|
||||||
@ -151,6 +153,8 @@ class BlMesh(BlDatablock):
|
|||||||
dumper.depth = 2
|
dumper.depth = 2
|
||||||
dumper.include_filter = [
|
dumper.include_filter = [
|
||||||
'name',
|
'name',
|
||||||
|
'use_auto_smooth',
|
||||||
|
'auto_smooth_angle'
|
||||||
]
|
]
|
||||||
data = dumper.dump(pointer)
|
data = dumper.dump(pointer)
|
||||||
dump_mesh(pointer, data)
|
dump_mesh(pointer, data)
|
||||||
|
@ -7,6 +7,7 @@ from .bl_datablock import BlDatablock
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def load_constraints(target, data):
|
def load_constraints(target, data):
|
||||||
for local_constraint in target.constraints:
|
for local_constraint in target.constraints:
|
||||||
if local_constraint.name not in data:
|
if local_constraint.name not in data:
|
||||||
@ -22,11 +23,13 @@ def load_constraints(target, data):
|
|||||||
utils.dump_anything.load(
|
utils.dump_anything.load(
|
||||||
target_constraint, data[constraint])
|
target_constraint, data[constraint])
|
||||||
|
|
||||||
|
|
||||||
def load_pose(target_bone, data):
|
def load_pose(target_bone, data):
|
||||||
target_bone.rotation_mode = data['rotation_mode']
|
target_bone.rotation_mode = data['rotation_mode']
|
||||||
|
|
||||||
utils.dump_anything.load(target_bone, data)
|
utils.dump_anything.load(target_bone, data)
|
||||||
|
|
||||||
|
|
||||||
class BlObject(BlDatablock):
|
class BlObject(BlDatablock):
|
||||||
bl_id = "objects"
|
bl_id = "objects"
|
||||||
bl_class = bpy.types.Object
|
bl_class = bpy.types.Object
|
||||||
@ -75,15 +78,20 @@ class BlObject(BlDatablock):
|
|||||||
if bpy.app.version[1] >= 83:
|
if bpy.app.version[1] >= 83:
|
||||||
pointer = bpy.data.lightprobes[data["data"]]
|
pointer = bpy.data.lightprobes[data["data"]]
|
||||||
else:
|
else:
|
||||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
logger.warning(
|
||||||
|
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||||
instance = bpy.data.objects.new(data["name"], pointer)
|
instance = bpy.data.objects.new(data["name"], pointer)
|
||||||
instance.uuid = self.uuid
|
instance.uuid = self.uuid
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def load_implementation(self, data, target):
|
def load_implementation(self, data, target):
|
||||||
if "matrix_world" in data:
|
# Load transformation data
|
||||||
target.matrix_world = mathutils.Matrix(data["matrix_world"])
|
rot_mode = 'rotation_quaternion' if data['rotation_mode'] == 'QUATERNION' else 'rotation_euler'
|
||||||
|
target.rotation_mode = data['rotation_mode']
|
||||||
|
target.location = data['location']
|
||||||
|
setattr(target, rot_mode, data[rot_mode])
|
||||||
|
target.scale = data['scale']
|
||||||
|
|
||||||
target.name = data["name"]
|
target.name = data["name"]
|
||||||
# Load modifiers
|
# Load modifiers
|
||||||
@ -106,7 +114,6 @@ class BlObject(BlDatablock):
|
|||||||
if hasattr(target, 'constraints') and 'constraints' in data:
|
if hasattr(target, 'constraints') and 'constraints' in data:
|
||||||
load_constraints(target, data['constraints'])
|
load_constraints(target, data['constraints'])
|
||||||
|
|
||||||
|
|
||||||
# Pose
|
# Pose
|
||||||
if 'pose' in data:
|
if 'pose' in data:
|
||||||
if not target.pose:
|
if not target.pose:
|
||||||
@ -131,12 +138,11 @@ class BlObject(BlDatablock):
|
|||||||
load_constraints(
|
load_constraints(
|
||||||
target_bone, bone_data['constraints'])
|
target_bone, bone_data['constraints'])
|
||||||
|
|
||||||
load_pose(target_bone,bone_data)
|
load_pose(target_bone, bone_data)
|
||||||
|
|
||||||
if 'bone_index' in bone_data.keys():
|
if 'bone_index' in bone_data.keys():
|
||||||
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']]
|
||||||
|
|
||||||
|
|
||||||
# Load relations
|
# Load relations
|
||||||
if 'children' in data.keys():
|
if 'children' in data.keys():
|
||||||
for child in data['children']:
|
for child in data['children']:
|
||||||
@ -171,7 +177,8 @@ class BlObject(BlDatablock):
|
|||||||
key_data = data['shape_keys']['key_blocks'][key_block]
|
key_data = data['shape_keys']['key_blocks'][key_block]
|
||||||
target.shape_key_add(name=key_block)
|
target.shape_key_add(name=key_block)
|
||||||
|
|
||||||
utils.dump_anything.load(target.data.shape_keys.key_blocks[key_block],key_data)
|
utils.dump_anything.load(
|
||||||
|
target.data.shape_keys.key_blocks[key_block], key_data)
|
||||||
for vert in key_data['data']:
|
for vert in key_data['data']:
|
||||||
target.data.shape_keys.key_blocks[key_block].data[vert].co = key_data['data'][vert]['co']
|
target.data.shape_keys.key_blocks[key_block].data[vert].co = key_data['data'][vert]['co']
|
||||||
|
|
||||||
@ -195,10 +202,11 @@ class BlObject(BlDatablock):
|
|||||||
"empty_display_type",
|
"empty_display_type",
|
||||||
"empty_display_size",
|
"empty_display_size",
|
||||||
"instance_collection",
|
"instance_collection",
|
||||||
"instance_type"
|
"instance_type",
|
||||||
|
"location",
|
||||||
|
"scale",
|
||||||
|
'rotation_quaternion' if pointer.rotation_mode == 'QUATERNION' else 'rotation_euler',
|
||||||
]
|
]
|
||||||
# if not utils.has_action(pointer):
|
|
||||||
dumper.include_filter.append('matrix_world')
|
|
||||||
|
|
||||||
data = dumper.dump(pointer)
|
data = dumper.dump(pointer)
|
||||||
|
|
||||||
@ -220,8 +228,6 @@ class BlObject(BlDatablock):
|
|||||||
dumper.depth = 3
|
dumper.depth = 3
|
||||||
data["constraints"] = dumper.dump(pointer.constraints)
|
data["constraints"] = dumper.dump(pointer.constraints)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# POSE
|
# POSE
|
||||||
if hasattr(pointer, 'pose') and pointer.pose:
|
if hasattr(pointer, 'pose') and pointer.pose:
|
||||||
# BONES
|
# BONES
|
||||||
@ -260,7 +266,6 @@ class BlObject(BlDatablock):
|
|||||||
bone_groups[group.name] = dumper.dump(group)
|
bone_groups[group.name] = dumper.dump(group)
|
||||||
data['pose']['bone_groups'] = bone_groups
|
data['pose']['bone_groups'] = bone_groups
|
||||||
|
|
||||||
|
|
||||||
# CHILDS
|
# CHILDS
|
||||||
if len(pointer.children) > 0:
|
if len(pointer.children) > 0:
|
||||||
childs = []
|
childs = []
|
||||||
@ -282,8 +287,6 @@ class BlObject(BlDatablock):
|
|||||||
for v in pointer.data.vertices:
|
for v in pointer.data.vertices:
|
||||||
for vg in v.groups:
|
for vg in v.groups:
|
||||||
if vg.group == vg_idx:
|
if vg.group == vg_idx:
|
||||||
# logger.error("VG {} : Adding vertex {} to group {}".format(vg_idx, v.index, vg_idx))
|
|
||||||
|
|
||||||
vertices.append({
|
vertices.append({
|
||||||
'index': v.index,
|
'index': v.index,
|
||||||
'weight': vg.weight
|
'weight': vg.weight
|
||||||
@ -337,7 +340,6 @@ class BlObject(BlDatablock):
|
|||||||
if self.is_library:
|
if self.is_library:
|
||||||
deps.append(self.pointer.library)
|
deps.append(self.pointer.library)
|
||||||
|
|
||||||
|
|
||||||
if self.pointer.instance_type == 'COLLECTION':
|
if self.pointer.instance_type == 'COLLECTION':
|
||||||
# TODO: uuid based
|
# TODO: uuid based
|
||||||
deps.append(self.pointer.instance_collection)
|
deps.append(self.pointer.instance_collection)
|
||||||
@ -346,5 +348,3 @@ class BlObject(BlDatablock):
|
|||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return bpy.data.objects.get(self.data['name'])
|
return bpy.data.objects.get(self.data['name'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from . import operators, presence, utils
|
from . import operators, presence, utils
|
||||||
from .libs.replication.replication.constants import FETCHED, RP_COMMON, STATE_ACTIVE, STATE_SYNCING, STATE_SRV_SYNC
|
from .libs.replication.replication.constants import FETCHED, RP_COMMON, STATE_INITIAL,STATE_QUITTING, STATE_ACTIVE, STATE_SYNCING, STATE_SRV_SYNC
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.WARNING)
|
logger.setLevel(logging.WARNING)
|
||||||
@ -217,6 +217,7 @@ class DrawClient(Draw):
|
|||||||
class ClientUpdate(Timer):
|
class ClientUpdate(Timer):
|
||||||
def __init__(self, timout=.5):
|
def __init__(self, timout=.5):
|
||||||
super().__init__(timout)
|
super().__init__(timout)
|
||||||
|
self.handle_quit = False
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
settings = bpy.context.window_manager.session
|
settings = bpy.context.window_manager.session
|
||||||
@ -276,6 +277,16 @@ class ClientUpdate(Timer):
|
|||||||
|
|
||||||
# TODO: event drivent 3d view refresh
|
# TODO: event drivent 3d view refresh
|
||||||
presence.refresh_3d_view()
|
presence.refresh_3d_view()
|
||||||
# ui update
|
elif session.state['STATE'] == STATE_QUITTING:
|
||||||
|
presence.refresh_3d_view()
|
||||||
|
self.handle_quit = True
|
||||||
|
elif session.state['STATE'] == STATE_INITIAL and self.handle_quit:
|
||||||
|
self.handle_quit = False
|
||||||
|
presence.refresh_3d_view()
|
||||||
|
|
||||||
|
operators.unregister_delayables()
|
||||||
|
|
||||||
|
presence.renderer.stop()
|
||||||
|
# # ui update
|
||||||
elif session:
|
elif session:
|
||||||
presence.refresh_3d_view()
|
presence.refresh_3d_view()
|
@ -114,6 +114,7 @@ class Dumper:
|
|||||||
self._match_type_matrix = (_dump_filter_type(mathutils.Matrix), self._dump_matrix)
|
self._match_type_matrix = (_dump_filter_type(mathutils.Matrix), self._dump_matrix)
|
||||||
self._match_type_vector = (_dump_filter_type(mathutils.Vector), self._dump_vector)
|
self._match_type_vector = (_dump_filter_type(mathutils.Vector), self._dump_vector)
|
||||||
self._match_type_quaternion = (_dump_filter_type(mathutils.Quaternion), self._dump_quaternion)
|
self._match_type_quaternion = (_dump_filter_type(mathutils.Quaternion), self._dump_quaternion)
|
||||||
|
self._match_type_euler = (_dump_filter_type(mathutils.Euler), self._dump_quaternion)
|
||||||
self._match_type_color = (_dump_filter_type_by_name("Color"), self._dump_color)
|
self._match_type_color = (_dump_filter_type_by_name("Color"), self._dump_color)
|
||||||
self._match_default = (_dump_filter_default, self._dump_default)
|
self._match_default = (_dump_filter_default, self._dump_default)
|
||||||
|
|
||||||
@ -193,6 +194,7 @@ class Dumper:
|
|||||||
self._match_type_matrix,
|
self._match_type_matrix,
|
||||||
self._match_type_vector,
|
self._match_type_vector,
|
||||||
self._match_type_quaternion,
|
self._match_type_quaternion,
|
||||||
|
self._match_type_euler,
|
||||||
self._match_type_color,
|
self._match_type_color,
|
||||||
self._match_default
|
self._match_default
|
||||||
]
|
]
|
||||||
@ -324,6 +326,9 @@ class Loader:
|
|||||||
def _load_quaternion(self, quaternion, dump):
|
def _load_quaternion(self, quaternion, dump):
|
||||||
quaternion.write(mathutils.Quaternion(dump))
|
quaternion.write(mathutils.Quaternion(dump))
|
||||||
|
|
||||||
|
def _load_euler(self, euler, dump):
|
||||||
|
euler.write(mathutils.Euler(dump))
|
||||||
|
|
||||||
def _ordered_keys(self, keys):
|
def _ordered_keys(self, keys):
|
||||||
ordered_keys = []
|
ordered_keys = []
|
||||||
for order_element in self.order:
|
for order_element in self.order:
|
||||||
@ -354,6 +359,7 @@ class Loader:
|
|||||||
(_load_filter_type(mathutils.Matrix, use_bl_rna=False), self._load_matrix), # before float because bl_rna type of matrix if FloatProperty
|
(_load_filter_type(mathutils.Matrix, use_bl_rna=False), self._load_matrix), # before float because bl_rna type of matrix if FloatProperty
|
||||||
(_load_filter_type(mathutils.Vector, use_bl_rna=False), self._load_vector), # before float because bl_rna type of vector if FloatProperty
|
(_load_filter_type(mathutils.Vector, use_bl_rna=False), self._load_vector), # before float because bl_rna type of vector if FloatProperty
|
||||||
(_load_filter_type(mathutils.Quaternion, use_bl_rna=False), self._load_quaternion),
|
(_load_filter_type(mathutils.Quaternion, use_bl_rna=False), self._load_quaternion),
|
||||||
|
(_load_filter_type(mathutils.Euler, use_bl_rna=False), self._load_euler),
|
||||||
(_load_filter_type(T.FloatProperty), self._load_identity),
|
(_load_filter_type(T.FloatProperty), self._load_identity),
|
||||||
(_load_filter_type(T.StringProperty), self._load_identity),
|
(_load_filter_type(T.StringProperty), self._load_identity),
|
||||||
(_load_filter_type(T.EnumProperty), self._load_identity),
|
(_load_filter_type(T.EnumProperty), self._load_identity),
|
||||||
|
Submodule multi_user/libs/replication updated: 343c9d527c...ed2f080bce
@ -30,6 +30,18 @@ ui_context = None
|
|||||||
stop_modal_executor = False
|
stop_modal_executor = False
|
||||||
modal_executor_queue = None
|
modal_executor_queue = None
|
||||||
server_process = None
|
server_process = None
|
||||||
|
|
||||||
|
def unregister_delayables():
|
||||||
|
global delayables, stop_modal_executor
|
||||||
|
|
||||||
|
for d in delayables:
|
||||||
|
try:
|
||||||
|
d.unregister()
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
stop_modal_executor = True
|
||||||
|
|
||||||
# OPERATORS
|
# OPERATORS
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +63,7 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
|
|
||||||
# TODO: Sync server clients
|
# TODO: Sync server clients
|
||||||
users.clear()
|
users.clear()
|
||||||
|
delayables.clear()
|
||||||
# save config
|
# save config
|
||||||
settings.save(context)
|
settings.save(context)
|
||||||
|
|
||||||
@ -105,8 +117,8 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.report({'ERROR'}, repr(e))
|
self.report({'ERROR'}, repr(e))
|
||||||
logger.error(f"Error: {e}")
|
logger.error(f"Error: {e}")
|
||||||
|
finally:
|
||||||
settings.is_admin = True
|
settings.is_admin = True
|
||||||
|
|
||||||
# Join a session
|
# Join a session
|
||||||
else:
|
else:
|
||||||
@ -122,6 +134,8 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.report({'ERROR'}, repr(e))
|
self.report({'ERROR'}, repr(e))
|
||||||
logger.error(f"Error: {e}")
|
logger.error(f"Error: {e}")
|
||||||
|
finally:
|
||||||
|
settings.is_admin = False
|
||||||
|
|
||||||
# Background client updates service
|
# Background client updates service
|
||||||
#TODO: Refactoring
|
#TODO: Refactoring
|
||||||
@ -158,18 +172,8 @@ class SessionStopOperator(bpy.types.Operator):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
global client, delayables, stop_modal_executor, server_process
|
global client, delayables, stop_modal_executor
|
||||||
assert(client)
|
assert(client)
|
||||||
stop_modal_executor = True
|
|
||||||
settings = context.window_manager.session
|
|
||||||
settings.is_admin = False
|
|
||||||
|
|
||||||
for d in delayables:
|
|
||||||
try:
|
|
||||||
d.unregister()
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
presence.renderer.stop()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
Reference in New Issue
Block a user