Merge branch 'user_metadata' into 'animation-support'

User metadata

See merge request slumber/multi-user!9
This commit is contained in:
Swann Martinez
2020-01-22 14:20:38 +00:00
9 changed files with 131 additions and 193 deletions
+5 -8
View File
@@ -57,22 +57,19 @@ def generate_supported_types():
def client_list_callback(scene, context):
from . import operators
from .bl_types.bl_user import BlUser
items = [(RP_COMMON, RP_COMMON, "")]
username = bpy.context.window_manager.session.username
cli = operators.client
if cli:
client_keys = cli.list(filter=BlUser)
for k in client_keys:
name = cli.get(uuid=k).data["name"]
name_desc = name
if name == username:
client_ids = cli.online_users.keys()
for id in client_ids:
name_desc = id
if id == username:
name_desc += " (self)"
items.append((name, name_desc, ""))
items.append((id, name_desc, ""))
return items
-1
View File
@@ -1,5 +1,4 @@
__all__ = [
'bl_user',
'bl_object',
'bl_mesh',
'bl_camera',
-64
View File
@@ -1,64 +0,0 @@
import bpy
import mathutils
import jsondiff
from .. import utils
from .. import presence
from .bl_datablock import BlDatablock
from ..libs.replication.replication.constants import UP
class BlUser(BlDatablock):
bl_id = "users"
bl_class = presence.User
bl_delay_refresh = .1
bl_delay_apply = .1
bl_automatic_push = True
bl_icon = 'CON_ARMATURE'
def construct(self, data):
user = bpy.data.window_managers['WinMan'].online_users.add()
user.name = data['name']
user.username = data['name']
user.current_frame = data['current_frame']
def apply(self):
if self.pointer is None:
self.set_pointer()
self.load(self.data,self.pointer)
for obj in bpy.data.objects:
if obj.hide_select and obj.uuid not in self.data['selected_objects']:
obj.hide_select = False
elif not obj.hide_select and obj.uuid in self.data['selected_objects']:
obj.hide_select = True
presence.refresh_3d_view()
self.state = UP
def load(self, data, target):
target.current_frame = data['current_frame']
def dump(self, pointer=None):
data = utils.dump_anything.dump(pointer)
data['location'] = pointer.location
data['color'] = pointer.color
data['selected_objects'] = pointer.selected_objects
data['view_matrix'] = pointer.view_matrix
data['current_frame'] = bpy.context.scene.frame_current
return data
def update(self):
self.pointer.is_dirty = True
def resolve(self):
self.pointer = bpy.data.window_managers['WinMan'].online_users.get(self.data['name'])
def is_valid(self):
return True
+91 -53
View File
@@ -3,10 +3,10 @@ import logging
import bpy
from . import operators, presence, utils
from .bl_types.bl_user import BlUser
from .libs.replication.replication.constants import FETCHED, RP_COMMON
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class Delayable():
@@ -83,26 +83,19 @@ class DynamicRightSelectTimer(Timer):
super().__init__(timout)
self._last_selection = []
self._user = None
self._user_node = None
self._right_strategy = RP_COMMON
def execute(self):
repo = operators.client
if repo:
settings = bpy.context.window_manager.session
session = operators.client
settings = bpy.context.window_manager.session
if session:
# Find user
if self._user is None:
users = repo.list(filter=BlUser)
for user in users:
user_node = repo.get(uuid=user)
if user_node.pointer:
self._user = user_node.pointer
self._user_node = user_node
self._user = session.online_users.get(settings.username)
if self._right_strategy is None:
self._right_strategy = repo.get_config()[
self._right_strategy = session.get_config()[
'right_strategy']
if self._user:
@@ -117,48 +110,65 @@ class DynamicRightSelectTimer(Timer):
# change old selection right to common
for obj in obj_common:
node = repo.get(uuid=obj)
node = session.get(uuid=obj)
if node and (node.owner == settings.username or node.owner == RP_COMMON):
recursive = True
if node.data and 'instance_type' in node.data.keys():
recursive = node.data['instance_type'] != 'COLLECTION'
repo.change_owner(
session.change_owner(
node.uuid,
RP_COMMON,
recursive=recursive)
# change new selection to our
for obj in obj_ours:
node = repo.get(uuid=obj)
node = session.get(uuid=obj)
if node and node.owner == RP_COMMON:
recursive = True
if node.data and 'instance_type' in node.data.keys():
recursive = node.data['instance_type'] != 'COLLECTION'
repo.change_owner(
session.change_owner(
node.uuid,
settings.username,
recursive=recursive)
else:
return
self._last_selection = current_selection
self._user.update_selected_objects(
bpy.context)
repo.push(self._user_node.uuid)
user_metadata = {
'selected_objects': current_selection
}
session.update_user_metadata(user_metadata)
logger.info("Update selection")
# Fix deselection until right managment refactoring (with Roles concepts)
if len(current_selection) == 0 and self._right_strategy == RP_COMMON:
owned_keys = repo.list(filter_owner=settings.username)
owned_keys = session.list(
filter_owner=settings.username)
for key in owned_keys:
node = repo.get(uuid=key)
if not isinstance(node, BlUser):
repo.change_owner(
key,
RP_COMMON,
recursive=recursive)
node = session.get(uuid=key)
session.change_owner(
key,
RP_COMMON,
recursive=recursive)
for user, user_info in session.online_users.items():
if user != settings.username:
metadata = user_info.get('metadata')
if 'selected_objects' in metadata:
# Update selectionnable objects
for obj in bpy.data.objects:
if obj.hide_select and obj.uuid not in metadata['selected_objects']:
obj.hide_select = False
elif not obj.hide_select and obj.uuid in metadata['selected_objects']:
obj.hide_select = True
class Draw(Delayable):
@@ -182,36 +192,59 @@ class Draw(Delayable):
class DrawClient(Draw):
def execute(self):
repo = operators.client
if repo and presence.renderer:
session = getattr(operators, 'client', None)
renderer = getattr(presence, 'renderer', None)
if session and renderer:
settings = bpy.context.window_manager.session
client_list = [key for key in repo.list(filter=BlUser) if
key != settings.user_uuid]
users = session.online_users
for cli in client_list:
cli_ref = repo.get(uuid=cli)
if cli_ref.data.get('name'):
if settings.presence_show_selected:
presence.renderer.draw_client_selection(
cli_ref.data['name'], cli_ref.data['color'], cli_ref.data['selected_objects'])
if settings.presence_show_user:
presence.renderer.draw_client_camera(
cli_ref.data['name'], cli_ref.data['location'], cli_ref.data['color'])
for user in users.values():
metadata = user.get('metadata')
if settings.presence_show_selected and 'selected_objects' in metadata.keys():
renderer.draw_client_selection(
user['id'], metadata['color'], metadata['selected_objects'])
if settings.presence_show_user and 'view_corners' in metadata:
renderer.draw_client_camera(
user['id'], metadata['view_corners'], metadata['color'])
class ClientUpdate(Timer):
def __init__(self, timout=1, client_uuid=None):
assert(client_uuid)
self._client_uuid = client_uuid
def __init__(self, timout=1):
super().__init__(timout)
def execute(self):
if self._client_uuid and operators.client:
client = operators.client.get(uuid=self._client_uuid)
settings = bpy.context.window_manager.session
session_info = bpy.context.window_manager.session
session = getattr(operators, 'client', None)
renderer = getattr(presence, 'renderer', None)
if session and renderer:
local_user = operators.client.online_users.get(
session_info.username)
if not local_user:
return
local_user_metadata = local_user.get('metadata')
current_view_corners = presence.get_view_corners()
if not local_user_metadata:
logger.info("init user metadata")
metadata = {
'view_corners': current_view_corners,
'view_matrix': presence.get_view_matrix(),
'color': (settings.client_color.r,
settings.client_color.g,
settings.client_color.b,
1),
}
session.update_user_metadata(metadata)
elif current_view_corners != local_user_metadata['view_corners']:
logger.info('update user metadata')
local_user_metadata['view_corners'] = current_view_corners
local_user_metadata['view_matrix'] = presence.get_view_matrix()
session.update_user_metadata(local_user_metadata)
if client:
client.pointer.update_location()
# sync online users
session_users = operators.client.online_users
ui_users = bpy.context.window_manager.online_users
@@ -219,13 +252,18 @@ class ClientUpdate(Timer):
for index, user in enumerate(ui_users):
if user.username not in session_users.keys():
ui_users.remove(index)
bpy.context.window_manager.session.presence_show_user = False
bpy.context.window_manager.session.presence_show_user = True
presence.refresh_3d_view()
renderer.flush_selection()
renderer.flush_users()
break
for user in session_users:
if user not in ui_users:
new_key = ui_users.add()
new_key.name = user
new_key.username = user
new_key.username = user
# TODO: event drivent 3d view refresh
presence.refresh_3d_view()
+11 -26
View File
@@ -36,7 +36,6 @@ modal_executor_queue = None
# OPERATORS
class SessionStartOperator(bpy.types.Operator):
bl_idname = "session.start"
bl_label = "start"
@@ -54,21 +53,15 @@ class SessionStartOperator(bpy.types.Operator):
users = bpy.data.window_managers['WinMan'].online_users
# TODO: Sync server clients
users.clear()
# #load our infos into the local user list
# local_user = users.add()
# local_user.name = 'localhost'
# local_user.username = settings.username
# local_user.current_frame = context.scene.frame_current
# save config
settings.save(context)
bpy_factory = ReplicatedDataFactory()
supported_bl_types = []
ui_context = context.copy()
# init the factory with supported types
for type in bl_types.types_to_register():
type_module = getattr(bl_types, type)
@@ -92,6 +85,8 @@ class SessionStartOperator(bpy.types.Operator):
target_type=type_module_class))
client = Session(factory=bpy_factory)
if self.host:
# Scene setup
@@ -123,26 +118,14 @@ class SessionStartOperator(bpy.types.Operator):
"A session is already hosted on this address")
return {"CANCELLED"}
# Init user settings
usr = presence.User(
username=settings.username,
color=(settings.client_color.r,
settings.client_color.g,
settings.client_color.b,
1),
)
settings.user_uuid = client.add(usr, owner=settings.username)
client.commit(settings.user_uuid)
if self.host:
for scene in bpy.data.scenes:
scene_uuid = client.add(scene)
# for node in client.list():
client.commit(scene_uuid)
delayables.append(delayable.ClientUpdate(
client_uuid=settings.user_uuid))
delayables.append(delayable.ClientUpdate())
delayables.append(delayable.DrawClient())
delayables.append(delayable.DynamicRightSelectTimer())
@@ -288,10 +271,12 @@ class SessionSnapUserOperator(bpy.types.Operator):
area, region, rv3d = presence.view3d_find()
global client
target_client = client.get(uuid=self.target_client)
if target_client:
rv3d.view_matrix = mathutils.Matrix(
target_client.data['view_matrix'])
if client:
target_ref = client.online_users.get(self.target_client)
if target_ref:
rv3d.view_matrix = mathutils.Matrix(
target_ref['metadata']['view_matrix'])
else:
return {"CANCELLED"}
+16 -34
View File
@@ -69,7 +69,7 @@ def get_default_bbox(obj, radius):
return [(point.x, point.y, point.z)
for point in bbox_corners]
def get_client_cam_points():
def get_view_corners():
area, region, rv3d = view3d_find()
v1 = [0, 0, 0]
@@ -113,32 +113,14 @@ def get_bb_coords_from_obj(object, parent=None):
return [(point.x, point.y, point.z)
for point in bbox_corners]
class User():
def __init__(self, username=None, color=(0, 0, 0, 1)):
self.is_dirty = False
self.name = username
self.color = color
self.location = [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
self.selected_objects = []
self.last_select_objects = []
self.view_matrix = None
def update_location(self):
current_coords = get_client_cam_points()
area, region, rv3d = view3d_find()
def get_view_matrix():
area, region, rv3d = view3d_find()
if area and region and rv3d:
current_coords = list(get_client_cam_points())
if current_coords and self.location != current_coords:
self.location = current_coords
if area and region and rv3d:
matrix_dumper = utils.dump_anything.Dumper()
matrix_dumper = utils.dump_anything.Dumper()
current_vm = matrix_dumper.dump(rv3d.view_matrix)
if self.view_matrix != current_vm:
self.view_matrix = current_vm
def update_selected_objects(self, context):
self.selected_objects = utils.get_selected_objects(context.scene)
return matrix_dumper.dump(rv3d.view_matrix)
def update_presence(self, context):
global renderer
@@ -217,14 +199,14 @@ class DrawFactory(object):
self.d2d_items.clear()
def draw_client_selection(self, client_uuid, client_color, client_selection):
local_user = bpy.context.window_manager.session.user_uuid
def draw_client_selection(self, client_id, client_color, client_selection):
local_user = bpy.context.window_manager.session.username
if local_user != client_uuid:
self.flush_selection(client_uuid)
if local_user != client_id:
self.flush_selection(client_id)
for select_ob in client_selection:
drawable_key = "{}_select_{}".format(client_uuid, select_ob)
drawable_key = "{}_select_{}".format(client_id, select_ob)
ob = utils.find_from_attr("uuid", select_ob, bpy.data.objects)
if not ob:
@@ -273,11 +255,11 @@ class DrawFactory(object):
self.d3d_items[key] = (shader, batch, color)
def draw_client_camera(self, client_uuid, client_location, client_color):
def draw_client_camera(self, client_id, client_location, client_color):
if client_location:
local_user = bpy.context.window_manager.session.user_uuid
local_user = bpy.context.window_manager.session.username
if local_user != client_uuid:
if local_user != client_id:
try:
indices = (
(1, 3), (2, 1), (3, 0),
@@ -292,8 +274,8 @@ class DrawFactory(object):
batch = batch_for_shader(
shader, 'LINES', {"pos": position}, indices=indices)
self.d3d_items[client_uuid] = (shader, batch, color)
self.d2d_items[client_uuid] = (position[1], client_uuid, color)
self.d3d_items[client_id] = (shader, batch, color)
self.d2d_items[client_id] = (position[1], client_id, color)
except Exception as e:
logger.error("Draw client exception {}".format(e))
+4 -5
View File
@@ -1,7 +1,6 @@
import bpy
from . import operators
from .bl_types.bl_user import BlUser
from .libs.replication.replication.constants import (ADDED, ERROR, FETCHED,
MODIFIED, RP_COMMON, UP)
@@ -188,7 +187,7 @@ class SESSION_PT_user(bpy.types.Panel):
# Create a simple row.
row = layout.row()
box = row.box()
split = box.split(factor=0.3)
split = box.split(factor=0.5)
split.label(text="user")
split.label(text="frame")
split.label(text="ping")
@@ -202,7 +201,7 @@ class SESSION_PT_user(bpy.types.Panel):
user_operations.operator(
"session.snapview",
text="",
icon='VIEW_CAMERA').target_client = online_users[selected_user].name
icon='VIEW_CAMERA').target_client = active_user.username
class SESSION_UL_users(bpy.types.UIList):
@@ -219,7 +218,7 @@ class SESSION_UL_users(bpy.types.UIList):
ping = str(user_stat['latency'])
break
split = layout.split(factor=0.3)
split = layout.split(factor=0.5)
split.label(text=item.username)
split.label(text=str(item.current_frame))
split.label(text=ping)
@@ -256,7 +255,7 @@ def draw_property(context, parent, property_uuid, level=0):
settings = context.window_manager.session
item = operators.client.get(uuid=property_uuid)
if item.str_type == 'BlUser' or item.state == ERROR:
if item.state == ERROR:
return
area_msg = parent.row(align=True)
+3 -1
View File
@@ -27,6 +27,7 @@ def has_driver(target):
and target.animation_data
and target.animation_data.drivers)
def find_from_attr(attr_name, attr_value, list):
for item in list:
if getattr(item, attr_name, None) == attr_value:
@@ -56,7 +57,7 @@ def get_datablock_users(datablock):
def random_string_digits(stringLength=6):
"""Generate a random string of letters and digits """
lettersAndDigits = string.ascii_letters + string.digits
return ''.join(random.choice(lettersAndDigits) for i in range(stringLength))
return ''.join(random.choices(lettersAndDigits, k=stringLength))
def clean_scene():
@@ -151,6 +152,7 @@ def dump_datablock_attibutes(datablock=None, attributes=[], depth=1, dickt=None)
return data
def resolve_from_id(id, optionnal_type=None):
for category in dir(bpy.data):
root = getattr(bpy.data, category)