2020-03-20 21:56:50 +08:00
|
|
|
# ##### 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 #####
|
|
|
|
|
2019-08-28 18:57:09 +08:00
|
|
|
import logging
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-09-30 19:35:50 +08:00
|
|
|
import bpy
|
|
|
|
|
|
|
|
from . import operators, presence, utils
|
2020-02-26 19:03:48 +08:00
|
|
|
from .libs.replication.replication.constants import FETCHED, RP_COMMON, STATE_INITIAL,STATE_QUITTING, STATE_ACTIVE, STATE_SYNCING, STATE_SRV_SYNC
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-09-27 20:50:00 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
class Delayable():
|
2019-08-14 21:01:30 +08:00
|
|
|
"""Delayable task interface
|
|
|
|
"""
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
def register(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def execute(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def unregister(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
class Timer(Delayable):
|
|
|
|
"""Timer binder interface for blender
|
|
|
|
|
|
|
|
Run a bpy.app.Timer in the background looping at the given rate
|
|
|
|
"""
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
def __init__(self, duration=1):
|
|
|
|
self._timeout = duration
|
2019-09-27 20:50:00 +08:00
|
|
|
self._running = True
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
def register(self):
|
|
|
|
"""Register the timer into the blender timer system
|
|
|
|
"""
|
2019-09-27 20:50:00 +08:00
|
|
|
bpy.app.timers.register(self.main)
|
|
|
|
|
|
|
|
def main(self):
|
|
|
|
self.execute()
|
2019-08-14 00:00:54 +08:00
|
|
|
|
2019-09-27 20:50:00 +08:00
|
|
|
if self._running:
|
|
|
|
return self._timeout
|
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
def execute(self):
|
|
|
|
"""Main timer loop
|
|
|
|
"""
|
2019-09-27 20:50:00 +08:00
|
|
|
raise NotImplementedError
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
def unregister(self):
|
|
|
|
"""Unnegister the timer of the blender timer system
|
|
|
|
"""
|
2019-09-27 20:50:00 +08:00
|
|
|
if bpy.app.timers.is_registered(self.main):
|
|
|
|
bpy.app.timers.unregister(self.main)
|
2019-08-14 00:00:54 +08:00
|
|
|
|
2019-09-27 20:50:00 +08:00
|
|
|
self._running = False
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-09-30 19:35:50 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
class ApplyTimer(Timer):
|
2019-08-23 18:28:57 +08:00
|
|
|
def __init__(self, timout=1, target_type=None):
|
2019-08-14 00:00:54 +08:00
|
|
|
self._type = target_type
|
|
|
|
super().__init__(timout)
|
|
|
|
|
|
|
|
def execute(self):
|
2020-02-14 23:02:30 +08:00
|
|
|
client = operators.client
|
|
|
|
if client and client.state['STATE'] == STATE_ACTIVE:
|
|
|
|
nodes = client.list(filter=self._type)
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
for node in nodes:
|
2020-02-14 23:02:30 +08:00
|
|
|
node_ref = client.get(uuid=node)
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
if node_ref.state == FETCHED:
|
2019-10-04 00:30:46 +08:00
|
|
|
try:
|
2020-02-14 23:02:30 +08:00
|
|
|
client.apply(node)
|
2019-10-14 19:08:31 +08:00
|
|
|
except Exception as e:
|
2020-04-22 23:04:14 +08:00
|
|
|
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
|
2019-09-19 05:55:30 +08:00
|
|
|
class DynamicRightSelectTimer(Timer):
|
2019-10-02 20:01:45 +08:00
|
|
|
def __init__(self, timout=.1):
|
2019-09-19 05:55:30 +08:00
|
|
|
super().__init__(timout)
|
2019-11-23 01:32:39 +08:00
|
|
|
self._last_selection = []
|
|
|
|
self._user = None
|
|
|
|
self._right_strategy = RP_COMMON
|
2019-09-19 05:55:30 +08:00
|
|
|
|
|
|
|
def execute(self):
|
2020-01-22 18:20:04 +08:00
|
|
|
session = operators.client
|
2020-03-02 18:09:45 +08:00
|
|
|
settings = utils.get_preferences()
|
2019-11-23 01:32:39 +08:00
|
|
|
|
2020-02-08 00:56:58 +08:00
|
|
|
if session and session.state['STATE'] == STATE_ACTIVE:
|
2019-11-23 01:32:39 +08:00
|
|
|
# Find user
|
|
|
|
if self._user is None:
|
2020-01-22 18:20:04 +08:00
|
|
|
self._user = session.online_users.get(settings.username)
|
2019-11-23 01:32:39 +08:00
|
|
|
|
|
|
|
if self._right_strategy is None:
|
2020-02-07 01:54:46 +08:00
|
|
|
self._right_strategy = session.config[
|
2019-11-23 01:32:39 +08:00
|
|
|
'right_strategy']
|
|
|
|
|
|
|
|
if self._user:
|
|
|
|
current_selection = utils.get_selected_objects(
|
2020-02-25 23:35:26 +08:00
|
|
|
bpy.context.scene,
|
|
|
|
bpy.data.window_managers['WinMan'].windows[0].view_layer
|
|
|
|
)
|
2019-11-23 01:32:39 +08:00
|
|
|
if current_selection != self._last_selection:
|
|
|
|
if self._right_strategy == RP_COMMON:
|
|
|
|
obj_common = [
|
|
|
|
o for o in self._last_selection if o not in current_selection]
|
|
|
|
obj_ours = [
|
|
|
|
o for o in current_selection if o not in self._last_selection]
|
|
|
|
|
|
|
|
# change old selection right to common
|
|
|
|
for obj in obj_common:
|
2020-01-22 18:20:04 +08:00
|
|
|
node = session.get(uuid=obj)
|
2019-11-23 01:32:39 +08:00
|
|
|
|
|
|
|
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'
|
2020-01-22 18:20:04 +08:00
|
|
|
session.change_owner(
|
2019-11-23 01:32:39 +08:00
|
|
|
node.uuid,
|
|
|
|
RP_COMMON,
|
|
|
|
recursive=recursive)
|
|
|
|
|
|
|
|
# change new selection to our
|
|
|
|
for obj in obj_ours:
|
2020-01-22 18:20:04 +08:00
|
|
|
node = session.get(uuid=obj)
|
2019-11-23 01:32:39 +08:00
|
|
|
|
|
|
|
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'
|
|
|
|
|
2020-01-22 18:20:04 +08:00
|
|
|
session.change_owner(
|
2019-11-23 01:32:39 +08:00
|
|
|
node.uuid,
|
|
|
|
settings.username,
|
|
|
|
recursive=recursive)
|
|
|
|
else:
|
|
|
|
return
|
2020-01-22 06:24:44 +08:00
|
|
|
|
2019-11-23 01:32:39 +08:00
|
|
|
self._last_selection = current_selection
|
|
|
|
|
2020-01-22 21:33:34 +08:00
|
|
|
user_metadata = {
|
|
|
|
'selected_objects': current_selection
|
|
|
|
}
|
|
|
|
|
|
|
|
session.update_user_metadata(user_metadata)
|
2020-04-22 23:04:14 +08:00
|
|
|
logging.debug("Update selection")
|
2020-01-22 21:33:34 +08:00
|
|
|
|
2019-11-23 01:32:39 +08:00
|
|
|
# Fix deselection until right managment refactoring (with Roles concepts)
|
|
|
|
if len(current_selection) == 0 and self._right_strategy == RP_COMMON:
|
2020-01-22 18:20:04 +08:00
|
|
|
owned_keys = session.list(
|
2020-01-22 06:24:44 +08:00
|
|
|
filter_owner=settings.username)
|
2019-11-23 01:32:39 +08:00
|
|
|
for key in owned_keys:
|
2020-01-22 18:20:04 +08:00
|
|
|
node = session.get(uuid=key)
|
2020-01-22 21:33:34 +08:00
|
|
|
|
|
|
|
session.change_owner(
|
|
|
|
key,
|
|
|
|
RP_COMMON,
|
|
|
|
recursive=recursive)
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2020-01-22 18:20:04 +08:00
|
|
|
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
|
|
|
|
|
2019-10-14 19:08:31 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
class Draw(Delayable):
|
|
|
|
def __init__(self):
|
|
|
|
self._handler = None
|
|
|
|
|
|
|
|
def register(self):
|
|
|
|
self._handler = bpy.types.SpaceView3D.draw_handler_add(
|
2019-08-23 18:28:57 +08:00
|
|
|
self.execute, (), 'WINDOW', 'POST_VIEW')
|
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
def execute(self):
|
|
|
|
raise NotImplementedError()
|
2019-08-23 18:28:57 +08:00
|
|
|
|
2019-08-14 00:00:54 +08:00
|
|
|
def unregister(self):
|
2019-08-14 03:32:15 +08:00
|
|
|
try:
|
|
|
|
bpy.types.SpaceView3D.draw_handler_remove(
|
2019-08-23 18:28:57 +08:00
|
|
|
self._handler, "WINDOW")
|
2019-08-14 03:32:15 +08:00
|
|
|
except:
|
2020-01-23 01:40:08 +08:00
|
|
|
pass
|
2019-08-23 18:28:57 +08:00
|
|
|
|
|
|
|
|
2019-10-02 20:01:45 +08:00
|
|
|
class DrawClient(Draw):
|
|
|
|
def execute(self):
|
2020-01-22 22:15:44 +08:00
|
|
|
session = getattr(operators, 'client', None)
|
|
|
|
renderer = getattr(presence, 'renderer', None)
|
2020-04-06 20:47:03 +08:00
|
|
|
prefs = utils.get_preferences()
|
2020-01-23 01:37:46 +08:00
|
|
|
|
2020-02-08 00:56:58 +08:00
|
|
|
if session and renderer and session.state['STATE'] == STATE_ACTIVE:
|
2019-10-02 20:01:45 +08:00
|
|
|
settings = bpy.context.window_manager.session
|
2020-01-22 06:24:44 +08:00
|
|
|
users = session.online_users
|
|
|
|
|
2020-04-06 20:47:03 +08:00
|
|
|
# Update users
|
2020-01-22 06:24:44 +08:00
|
|
|
for user in users.values():
|
|
|
|
metadata = user.get('metadata')
|
2020-03-06 00:20:04 +08:00
|
|
|
color = metadata.get('color')
|
|
|
|
scene_current = metadata.get('scene_current')
|
|
|
|
user_showable = scene_current == bpy.context.scene.name or settings.presence_show_far_user
|
|
|
|
if color and scene_current and user_showable:
|
2020-02-08 00:08:36 +08:00
|
|
|
if settings.presence_show_selected and 'selected_objects' in metadata.keys():
|
|
|
|
renderer.draw_client_selection(
|
2020-03-06 00:20:04 +08:00
|
|
|
user['id'], color, metadata['selected_objects'])
|
2020-02-08 00:08:36 +08:00
|
|
|
if settings.presence_show_user and 'view_corners' in metadata:
|
|
|
|
renderer.draw_client_camera(
|
2020-03-06 00:20:04 +08:00
|
|
|
user['id'], metadata['view_corners'], color)
|
|
|
|
if not user_showable:
|
|
|
|
# TODO: remove this when user event drivent update will be
|
|
|
|
# ready
|
|
|
|
renderer.flush_selection()
|
|
|
|
renderer.flush_users()
|
2019-10-02 20:01:45 +08:00
|
|
|
|
2020-04-06 20:47:03 +08:00
|
|
|
|
2019-10-03 19:23:59 +08:00
|
|
|
class ClientUpdate(Timer):
|
2020-04-06 20:47:03 +08:00
|
|
|
def __init__(self, timout=.016):
|
2019-10-03 19:23:59 +08:00
|
|
|
super().__init__(timout)
|
2020-02-26 19:03:48 +08:00
|
|
|
self.handle_quit = False
|
2020-04-06 20:47:03 +08:00
|
|
|
self.users_metadata = {}
|
2019-08-14 00:00:54 +08:00
|
|
|
|
|
|
|
def execute(self):
|
2020-03-02 18:09:45 +08:00
|
|
|
settings = utils.get_preferences()
|
2020-01-22 22:15:44 +08:00
|
|
|
session = getattr(operators, 'client', None)
|
|
|
|
renderer = getattr(presence, 'renderer', None)
|
2020-02-26 19:03:48 +08:00
|
|
|
|
2020-04-15 00:56:20 +08:00
|
|
|
if session and renderer:
|
|
|
|
if session.state['STATE'] == STATE_ACTIVE:
|
|
|
|
# Check if session has been closes prematurely
|
|
|
|
if session.state['STATE'] == 0:
|
|
|
|
bpy.ops.session.stop()
|
2020-01-23 01:37:46 +08:00
|
|
|
|
2020-04-15 00:56:20 +08:00
|
|
|
local_user = operators.client.online_users.get(settings.username)
|
2020-04-06 20:47:03 +08:00
|
|
|
|
2020-04-15 00:56:20 +08:00
|
|
|
if not local_user:
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
for username, user_data in operators.client.online_users.items():
|
|
|
|
if username != settings.username:
|
|
|
|
cached_user_data = self.users_metadata.get(username)
|
|
|
|
new_user_data = operators.client.online_users[username]['metadata']
|
|
|
|
|
|
|
|
if cached_user_data is None:
|
|
|
|
self.users_metadata[username] = user_data['metadata']
|
|
|
|
elif 'view_matrix' in cached_user_data and 'view_matrix' in new_user_data and cached_user_data['view_matrix'] != new_user_data['view_matrix']:
|
|
|
|
presence.refresh_3d_view()
|
|
|
|
self.users_metadata[username] = user_data['metadata']
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.users_metadata[username] = user_data['metadata']
|
|
|
|
|
|
|
|
local_user_metadata = local_user.get('metadata')
|
|
|
|
scene_current = bpy.context.scene.name
|
|
|
|
local_user = session.online_users.get(settings.username)
|
|
|
|
current_view_corners = presence.get_view_corners()
|
|
|
|
|
|
|
|
# Init client metadata
|
|
|
|
if not local_user_metadata or 'color' not in local_user_metadata.keys():
|
|
|
|
metadata = {
|
|
|
|
'view_corners': presence.get_view_matrix(),
|
|
|
|
'view_matrix': presence.get_view_matrix(),
|
|
|
|
'color': (settings.client_color.r,
|
|
|
|
settings.client_color.g,
|
|
|
|
settings.client_color.b,
|
|
|
|
1),
|
|
|
|
'frame_current':bpy.context.scene.frame_current,
|
|
|
|
'scene_current': scene_current
|
|
|
|
}
|
|
|
|
session.update_user_metadata(metadata)
|
|
|
|
|
|
|
|
# Update client representation
|
|
|
|
# Update client current scene
|
|
|
|
elif scene_current != local_user_metadata['scene_current']:
|
|
|
|
local_user_metadata['scene_current'] = scene_current
|
|
|
|
session.update_user_metadata(local_user_metadata)
|
|
|
|
elif 'view_corners' in local_user_metadata and current_view_corners != local_user_metadata['view_corners']:
|
|
|
|
local_user_metadata['view_corners'] = current_view_corners
|
|
|
|
local_user_metadata['view_matrix'] = presence.get_view_matrix()
|
|
|
|
session.update_user_metadata(local_user_metadata)
|
|
|
|
# sync online users
|
|
|
|
session_users = operators.client.online_users
|
|
|
|
ui_users = bpy.context.window_manager.online_users
|
|
|
|
|
|
|
|
for index, user in enumerate(ui_users):
|
|
|
|
if user.username not in session_users.keys():
|
|
|
|
ui_users.remove(index)
|
|
|
|
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
|
|
|
|
elif session.state['STATE'] == STATE_QUITTING:
|
|
|
|
presence.refresh_sidebar_view()
|
|
|
|
self.handle_quit = True
|
|
|
|
elif session.state['STATE'] == STATE_INITIAL and self.handle_quit:
|
|
|
|
self.handle_quit = False
|
|
|
|
presence.refresh_sidebar_view()
|
|
|
|
|
|
|
|
operators.unregister_delayables()
|
|
|
|
|
|
|
|
presence.renderer.stop()
|
2020-02-26 19:03:48 +08:00
|
|
|
|
2020-04-15 00:56:20 +08:00
|
|
|
presence.refresh_sidebar_view()
|