Merge branch 'user_metadata' into 'animation-support'
User metadata See merge request slumber/multi-user!9
This commit is contained in:
@@ -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,5 +1,4 @@
|
||||
__all__ = [
|
||||
'bl_user',
|
||||
'bl_object',
|
||||
'bl_mesh',
|
||||
'bl_camera',
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
Submodule multi_user/libs/replication updated: f70b4f67a6...2060bb6efb
+11
-26
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user