Merge branch 'develop' into feature/event_driven_updates
This commit is contained in:
@@ -51,18 +51,21 @@ DEPENDENCIES = {
|
||||
}
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
|
||||
libs = os.path.dirname(os.path.abspath(__file__))+"\\libs\\replication\\replication"
|
||||
|
||||
def register():
|
||||
# Setup logging policy
|
||||
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
|
||||
|
||||
if libs not in sys.path:
|
||||
sys.path.append(libs)
|
||||
|
||||
environment.setup(DEPENDENCIES,bpy.app.binary_path_python)
|
||||
|
||||
try:
|
||||
environment.setup(DEPENDENCIES, bpy.app.binary_path_python)
|
||||
except ModuleNotFoundError:
|
||||
logging.fatal("Fail to install multi-user dependencies, try to execute blender with admin rights.")
|
||||
return
|
||||
|
||||
from . import presence
|
||||
from . import operators
|
||||
from . import ui
|
||||
|
||||
@@ -27,7 +27,6 @@ from .dump_anything import (Dumper, Loader,
|
||||
np_load_collection,
|
||||
np_dump_collection)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SPLINE_BEZIER_POINT = [
|
||||
# "handle_left_type",
|
||||
@@ -77,7 +76,7 @@ class BlCurve(BlDatablock):
|
||||
# Not really working for now...
|
||||
# See https://blender.stackexchange.com/questions/7020/create-nurbs-surface-with-python
|
||||
if new_spline.type == 'NURBS':
|
||||
logger.error("NURBS not supported.")
|
||||
logging.error("NURBS not supported.")
|
||||
# new_spline.points.add(len(data['splines'][spline]["points"])-1)
|
||||
# for point_index in data['splines'][spline]["points"]:
|
||||
# loader.load(
|
||||
|
||||
@@ -111,8 +111,8 @@ class BlDatablock(ReplicatedDatablock):
|
||||
|
||||
# self.diff_method = DIFF_BINARY
|
||||
|
||||
@property
|
||||
def instance(self):
|
||||
|
||||
def _resolve(self):
|
||||
datablock_ref = None
|
||||
datablock_root = getattr(bpy.data, self.bl_id)
|
||||
datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root)
|
||||
|
||||
@@ -19,25 +19,26 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
import os
|
||||
|
||||
import logging
|
||||
from .. import utils
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
def dump_image(image):
|
||||
pixels = None
|
||||
if image.source == "GENERATED":
|
||||
if image.source == "GENERATED" or image.packed_file is not None:
|
||||
prefs = utils.get_preferences()
|
||||
img_name = "{}.png".format(image.name)
|
||||
img_name = f"{image.name}.png"
|
||||
|
||||
# Cache the image on the disk
|
||||
image.filepath_raw = os.path.join(prefs.cache_directory, img_name)
|
||||
os.makedirs(prefs.cache_directory, exist_ok=True)
|
||||
image.file_format = "PNG"
|
||||
image.save()
|
||||
logging.info( image.filepath_raw )
|
||||
|
||||
if image.source == "FILE":
|
||||
image_path = bpy.path.abspath(image.filepath_raw)
|
||||
image_path = bpy.path.abspath(image.filepath_raw)
|
||||
image_directory = os.path.dirname(image_path)
|
||||
os.makedirs(image_directory, exist_ok=True)
|
||||
image.save()
|
||||
@@ -67,10 +68,10 @@ class BlImage(BlDatablock):
|
||||
image = target
|
||||
prefs = utils.get_preferences()
|
||||
|
||||
img_name = "{}.png".format(image.name)
|
||||
|
||||
img_path = os.path.join(prefs.cache_directory, img_name)
|
||||
img_name = f"{image.name}.png"
|
||||
|
||||
img_path = os.path.join(prefs.cache_directory,img_name)
|
||||
os.makedirs(prefs.cache_directory, exist_ok=True)
|
||||
file = open(img_path, 'wb')
|
||||
file.write(data["pixels"])
|
||||
file.close()
|
||||
|
||||
@@ -21,6 +21,7 @@ import mathutils
|
||||
|
||||
from .dump_anything import Dumper, Loader, np_dump_collection, np_load_collection
|
||||
from .bl_datablock import BlDatablock
|
||||
from ..libs.replication.replication.exception import ContextError
|
||||
|
||||
POINT = ['co', 'weight_softbody', 'co_deform']
|
||||
|
||||
@@ -37,13 +38,17 @@ class BlLattice(BlDatablock):
|
||||
return bpy.data.lattices.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
if target.is_editmode:
|
||||
raise ContextError("lattice is in edit mode")
|
||||
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
np_load_collection(data['points'], target.points, POINT)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
if instance.is_editmode:
|
||||
raise ContextError("lattice is in edit mode")
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
@@ -61,5 +66,6 @@ class BlLattice(BlDatablock):
|
||||
data = dumper.dump(instance)
|
||||
|
||||
data['points'] = np_dump_collection(instance.points, POINT)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ class BlLight(BlDatablock):
|
||||
"shape",
|
||||
"size_y",
|
||||
"size",
|
||||
"angle"
|
||||
"angle",
|
||||
'spot_size',
|
||||
'spot_blend'
|
||||
]
|
||||
data = dumper.dump(instance)
|
||||
return data
|
||||
|
||||
@@ -23,7 +23,6 @@ import logging
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BlLightprobe(BlDatablock):
|
||||
bl_id = "lightprobes"
|
||||
@@ -39,7 +38,7 @@ class BlLightprobe(BlDatablock):
|
||||
if bpy.app.version[1] >= 83:
|
||||
return bpy.data.lightprobes.new(data["name"], type)
|
||||
else:
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
loader = Loader()
|
||||
@@ -48,7 +47,7 @@ class BlLightprobe(BlDatablock):
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
if bpy.app.version[1] < 83:
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
|
||||
@@ -24,7 +24,6 @@ from .. import utils
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def load_node(node_data, node_tree):
|
||||
""" Load a node into a node_tree from a dict
|
||||
@@ -46,7 +45,7 @@ def load_node(node_data, node_tree):
|
||||
try:
|
||||
target_node.inputs[input].default_value = node_data["inputs"][input]["default_value"]
|
||||
except:
|
||||
logger.error("{} not supported, skipping".format(input))
|
||||
logging.error(f"Material {input} parameter not supported, skipping")
|
||||
|
||||
|
||||
def load_links(links_data, node_tree):
|
||||
|
||||
@@ -24,9 +24,9 @@ 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 ..libs.replication.replication.constants import DIFF_BINARY
|
||||
from ..libs.replication.replication.exception import ContextError
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VERTICE = ['co']
|
||||
|
||||
@@ -111,7 +111,9 @@ class BlMesh(BlDatablock):
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
|
||||
|
||||
if instance.is_editmode:
|
||||
raise ContextError("Mesh is in edit mode")
|
||||
mesh = instance
|
||||
|
||||
dumper = Dumper()
|
||||
|
||||
@@ -22,8 +22,7 @@ import logging
|
||||
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
from ..libs.replication.replication.exception import ContextError
|
||||
|
||||
|
||||
def load_pose(target_bone, data):
|
||||
@@ -80,7 +79,7 @@ class BlObject(BlDatablock):
|
||||
if bpy.app.version[1] >= 83:
|
||||
instance = bpy.data.lightprobes[data["data"]]
|
||||
else:
|
||||
logger.warning(
|
||||
logging.warning(
|
||||
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
instance = bpy.data.objects.new(data["name"], instance)
|
||||
instance.uuid = self.uuid
|
||||
@@ -126,7 +125,8 @@ class BlObject(BlDatablock):
|
||||
target.vertex_groups.clear()
|
||||
for vg in data['vertex_groups']:
|
||||
vertex_group = target.vertex_groups.new(name=vg['name'])
|
||||
for vert in vg['vertices']:
|
||||
point_attr = 'vertices' if 'vertices' in vg else 'points'
|
||||
for vert in vg[point_attr]:
|
||||
vertex_group.add(
|
||||
[vert['index']], vert['weight'], 'REPLACE')
|
||||
|
||||
@@ -154,6 +154,12 @@ class BlObject(BlDatablock):
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
|
||||
child_data = getattr(instance, 'data', None)
|
||||
|
||||
if child_data and hasattr(child_data, 'is_editmode') and child_data.is_editmode:
|
||||
raise ContextError("Object is in edit-mode.")
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
dumper.include_filter = [
|
||||
@@ -239,6 +245,7 @@ class BlObject(BlDatablock):
|
||||
|
||||
# VERTEx GROUP
|
||||
if len(instance.vertex_groups) > 0:
|
||||
points_attr = 'vertices' if isinstance(instance.data, bpy.types.Mesh) else 'points'
|
||||
vg_data = []
|
||||
for vg in instance.vertex_groups:
|
||||
vg_idx = vg.index
|
||||
@@ -247,11 +254,11 @@ class BlObject(BlDatablock):
|
||||
|
||||
vertices = []
|
||||
|
||||
for v in instance.data.vertices:
|
||||
for i, v in enumerate(getattr(instance.data, points_attr)):
|
||||
for vg in v.groups:
|
||||
if vg.group == vg_idx:
|
||||
vertices.append({
|
||||
'index': v.index,
|
||||
'index': i,
|
||||
'weight': vg.weight
|
||||
})
|
||||
|
||||
|
||||
@@ -126,9 +126,9 @@ class BlScene(BlDatablock):
|
||||
data['eevee'] = scene_dumper.dump(instance.eevee)
|
||||
data['cycles'] = scene_dumper.dump(instance.cycles)
|
||||
data['view_settings'] = scene_dumper.dump(instance.view_settings)
|
||||
data['view_settings']['curve_mapping'] = scene_dumper.dump(instance.view_settings.curve_mapping)
|
||||
|
||||
|
||||
if instance.view_settings.use_curve_mapping:
|
||||
data['view_settings']['curve_mapping'] = scene_dumper.dump(instance.view_settings.curve_mapping)
|
||||
scene_dumper.depth = 5
|
||||
scene_dumper.include_filter = [
|
||||
'curves',
|
||||
|
||||
@@ -22,7 +22,6 @@ import bpy.types as T
|
||||
import mathutils
|
||||
import numpy as np
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BPY_TO_NUMPY_TYPES = {
|
||||
'FLOAT': np.float,
|
||||
@@ -48,7 +47,7 @@ def np_load_collection(dikt: dict, collection: bpy.types.CollectionProperty, att
|
||||
:type attributes: list
|
||||
"""
|
||||
if not dikt or len(collection) == 0:
|
||||
logger.warning(f'Skipping collection')
|
||||
logging.warning(f'Skipping collection')
|
||||
return
|
||||
|
||||
if attributes is None:
|
||||
@@ -62,7 +61,7 @@ def np_load_collection(dikt: dict, collection: bpy.types.CollectionProperty, att
|
||||
elif attr_type == 'ENUM':
|
||||
np_load_collection_enum(collection, attr, dikt[attr])
|
||||
else:
|
||||
logger.error(f"{attr} of type {attr_type} not supported.")
|
||||
logging.error(f"{attr} of type {attr_type} not supported.")
|
||||
|
||||
|
||||
def np_dump_collection(collection: bpy.types.CollectionProperty, attributes: list = None) -> dict:
|
||||
@@ -98,7 +97,7 @@ def np_dump_collection(collection: bpy.types.CollectionProperty, attributes: lis
|
||||
elif attr_type == 'ENUM':
|
||||
dumped_collection[attr] = np_dump_collection_enum(collection, attr)
|
||||
else:
|
||||
logger.error(f"{attr} of type {attr_type} not supported. Only {PRIMITIVE_TYPES} and ENUM supported. Skipping it.")
|
||||
logging.error(f"{attr} of type {attr_type} not supported. Only {PRIMITIVE_TYPES} and ENUM supported. Skipping it.")
|
||||
|
||||
return dumped_collection
|
||||
|
||||
@@ -116,7 +115,7 @@ def np_dump_collection_primitive(collection: bpy.types.CollectionProperty, attri
|
||||
:return: numpy byte buffer
|
||||
"""
|
||||
if len(collection) == 0:
|
||||
logger.warning(f'Skipping empty {attribute} attribute')
|
||||
logging.warning(f'Skipping empty {attribute} attribute')
|
||||
return {}
|
||||
|
||||
attr_infos = collection[0].bl_rna.properties.get(attribute)
|
||||
@@ -193,7 +192,7 @@ def np_load_collection_primitives(collection: bpy.types.CollectionProperty, attr
|
||||
:type sequence: strr
|
||||
"""
|
||||
if len(collection) == 0 or not sequence:
|
||||
logger.warning(f"Skipping loadin {attribute}")
|
||||
logging.warning(f"Skipping loadin {attribute}")
|
||||
return
|
||||
|
||||
attr_infos = collection[0].bl_rna.properties.get(attribute)
|
||||
@@ -379,7 +378,7 @@ class Dumper:
|
||||
return False
|
||||
getattr(default, p)
|
||||
except AttributeError as err:
|
||||
logger.debug(err)
|
||||
logging.debug(err)
|
||||
return False
|
||||
if p.startswith("__"):
|
||||
return False
|
||||
@@ -489,7 +488,7 @@ class Loader:
|
||||
for i in range(len(dump)):
|
||||
element.read()[i] = dump[i]
|
||||
except AttributeError as err:
|
||||
logger.debug(err)
|
||||
logging.debug(err)
|
||||
if not self.occlude_read_only:
|
||||
raise err
|
||||
|
||||
@@ -541,7 +540,7 @@ class Loader:
|
||||
_constructor_parameters = [dumped_element[name]
|
||||
for name in _constructor[1]]
|
||||
except KeyError:
|
||||
logger.debug("Collection load error, missing parameters.")
|
||||
logging.debug("Collection load error, missing parameters.")
|
||||
continue # TODO handle error
|
||||
|
||||
new_element = getattr(element.read(), _constructor[0])(
|
||||
@@ -623,11 +622,11 @@ class Loader:
|
||||
for k in self._ordered_keys(dump.keys()):
|
||||
v = dump[k]
|
||||
if not hasattr(default.read(), k):
|
||||
logger.debug(f"Load default, skipping {default} : {k}")
|
||||
logging.debug(f"Load default, skipping {default} : {k}")
|
||||
try:
|
||||
self._load_any(default.extend(k), v)
|
||||
except Exception as err:
|
||||
logger.debug(f"Cannot load {k}: {err}")
|
||||
logging.debug(f"Cannot load {k}: {err}")
|
||||
|
||||
@property
|
||||
def match_subset_all(self):
|
||||
|
||||
+76
-85
@@ -22,9 +22,6 @@ import bpy
|
||||
from . import operators, presence, utils
|
||||
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.setLevel(logging.WARNING)
|
||||
|
||||
|
||||
class Delayable():
|
||||
"""Delayable task interface
|
||||
@@ -91,8 +88,7 @@ class ApplyTimer(Timer):
|
||||
try:
|
||||
client.apply(node, force=True)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"fail to apply {}: {}".format(node_ref.uuid, e))
|
||||
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
||||
|
||||
|
||||
class DynamicRightSelectTimer(Timer):
|
||||
@@ -163,7 +159,7 @@ class DynamicRightSelectTimer(Timer):
|
||||
}
|
||||
|
||||
session.update_user_metadata(user_metadata)
|
||||
logger.info("Update selection")
|
||||
logging.debug("Update selection")
|
||||
|
||||
# Fix deselection until right managment refactoring (with Roles concepts)
|
||||
if len(current_selection) == 0 and self._right_strategy == RP_COMMON:
|
||||
@@ -250,89 +246,84 @@ class ClientUpdate(Timer):
|
||||
session = getattr(operators, 'client', None)
|
||||
renderer = getattr(presence, 'renderer', None)
|
||||
|
||||
if session and renderer and session.state['STATE'] == STATE_ACTIVE:
|
||||
# Check if session has been closes prematurely
|
||||
if session.state['STATE'] == 0:
|
||||
bpy.ops.session.stop()
|
||||
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()
|
||||
|
||||
local_user = operators.client.online_users.get(settings.username)
|
||||
|
||||
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()
|
||||
local_user = operators.client.online_users.get(settings.username)
|
||||
|
||||
# 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)
|
||||
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']
|
||||
|
||||
# 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
|
||||
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)
|
||||
|
||||
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
|
||||
# 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 user in session_users:
|
||||
if user not in ui_users:
|
||||
new_key = ui_users.add()
|
||||
new_key.name = user
|
||||
new_key.username = user
|
||||
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
|
||||
|
||||
# TODO: event drivent 3d view refresh
|
||||
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()
|
||||
|
||||
|
||||
|
||||
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.state['STATE'] != STATE_INITIAL:
|
||||
presence.refresh_3d_view()
|
||||
presence.refresh_sidebar_view()
|
||||
@@ -23,8 +23,6 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
THIRD_PARTY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs")
|
||||
DEFAULT_CACHE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cache")
|
||||
@@ -49,8 +47,9 @@ def install_pip():
|
||||
|
||||
|
||||
def install_package(name):
|
||||
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install",
|
||||
name], cwd=SUBPROCESS_DIR)
|
||||
logging.debug(f"Using {PYTHON_PATH} for installation")
|
||||
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", name])
|
||||
|
||||
|
||||
def check_dir(dir):
|
||||
if not os.path.exists(dir):
|
||||
@@ -68,3 +67,4 @@ def setup(dependencies, python_path):
|
||||
for module_name, package_name in dependencies:
|
||||
if not module_can_be_imported(module_name):
|
||||
install_package(package_name)
|
||||
module_can_be_imported(package_name)
|
||||
|
||||
Submodule multi_user/libs/replication updated: 4d2f575b24...e991835a15
+6
-14
@@ -39,8 +39,6 @@ from .libs.replication.replication.data import ReplicatedDataFactory
|
||||
from .libs.replication.replication.exception import NonAuthorizedOperationError
|
||||
from .libs.replication.replication.interface import Session
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
client = None
|
||||
delayables = []
|
||||
@@ -91,7 +89,7 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
# init the factory with supported types
|
||||
for type in bl_types.types_to_register():
|
||||
type_module = getattr(bl_types, type)
|
||||
type_impl_name = "Bl{}".format(type.split('_')[1].capitalize())
|
||||
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
||||
type_module_class = getattr(type_module, type_impl_name)
|
||||
|
||||
supported_bl_types.append(type_module_class.bl_id)
|
||||
@@ -132,7 +130,7 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, repr(e))
|
||||
logger.error(f"Error: {e}")
|
||||
logging.error(f"Error: {e}")
|
||||
finally:
|
||||
runtime_settings.is_admin = True
|
||||
|
||||
@@ -150,7 +148,7 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, repr(e))
|
||||
logger.error(f"Error: {e}")
|
||||
logging.error(f"Error: {e}")
|
||||
finally:
|
||||
runtime_settings.is_admin = False
|
||||
|
||||
@@ -174,7 +172,7 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
|
||||
self.report(
|
||||
{'INFO'},
|
||||
"connexion on tcp://{}:{}".format(settings.ip, settings.port))
|
||||
f"connecting to tcp://{settings.ip}:{settings.port}")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -464,8 +462,7 @@ class ApplyArmatureOperator(bpy.types.Operator):
|
||||
try:
|
||||
client.apply(node)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"fail to apply {}: {}".format(node_ref.uuid, e))
|
||||
logging.error("Dail to apply armature: {e}")
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
@@ -579,9 +576,4 @@ def unregister():
|
||||
|
||||
|
||||
bpy.app.handlers.frame_change_pre.remove(update_client_frame)
|
||||
|
||||
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation)
|
||||
+59
-20
@@ -20,10 +20,10 @@ import logging
|
||||
import bpy
|
||||
import string
|
||||
|
||||
from . import utils, bl_types, environment, addon_updater_ops, presence
|
||||
from .libs.replication.replication.constants import RP_COMMON
|
||||
from . import utils, bl_types, environment, addon_updater_ops, presence, ui
|
||||
from .libs.replication.replication.constants import RP_COMMON
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def randomColor():
|
||||
"""Generate a random color """
|
||||
@@ -37,7 +37,13 @@ def random_string_digits(stringLength=6):
|
||||
"""Generate a random string of letters and digits """
|
||||
lettersAndDigits = string.ascii_letters + string.digits
|
||||
return ''.join(random.choices(lettersAndDigits, k=stringLength))
|
||||
|
||||
|
||||
|
||||
def update_panel_category(self, context):
|
||||
ui.unregister()
|
||||
ui.SESSION_PT_settings.bl_category = self.panel_category
|
||||
ui.register()
|
||||
|
||||
|
||||
class ReplicatedDatablock(bpy.types.PropertyGroup):
|
||||
type_name: bpy.props.StringProperty()
|
||||
@@ -117,6 +123,18 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
],
|
||||
default='CONFIG'
|
||||
)
|
||||
# WIP
|
||||
logging_level: bpy.props.EnumProperty(
|
||||
name="Log level",
|
||||
description="Log verbosity level",
|
||||
items=[
|
||||
('ERROR', "error", "show only errors"),
|
||||
('WARNING', "warning", "only show warnings and errors"),
|
||||
('INFO', "info", "default level"),
|
||||
('DEBUG', "debug", "show all logs"),
|
||||
],
|
||||
default='INFO'
|
||||
)
|
||||
conf_session_identity_expanded: bpy.props.BoolProperty(
|
||||
name="Identity",
|
||||
description="Identity",
|
||||
@@ -142,6 +160,11 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
description="cache",
|
||||
default=False
|
||||
)
|
||||
conf_session_ui_expanded: bpy.props.BoolProperty(
|
||||
name="Interface",
|
||||
description="Interface",
|
||||
default=False
|
||||
)
|
||||
|
||||
auto_check_update: bpy.props.BoolProperty(
|
||||
name="Auto-check for Update",
|
||||
@@ -176,14 +199,20 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
max=59
|
||||
)
|
||||
|
||||
# Custom panel
|
||||
panel_category: bpy.props.StringProperty(
|
||||
description="Choose a name for the category of the panel",
|
||||
default="Multiuser",
|
||||
update=update_panel_category)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.row().prop(self, "category", expand=True)
|
||||
|
||||
|
||||
if self.category == 'CONFIG':
|
||||
grid = layout.column()
|
||||
|
||||
|
||||
# USER INFORMATIONS
|
||||
box = grid.box()
|
||||
box.prop(
|
||||
@@ -215,7 +244,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
self, "conf_session_timing_expanded", text="Refresh rates",
|
||||
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_timing_expanded
|
||||
else 'DISCLOSURE_TRI_RIGHT', emboss=False)
|
||||
|
||||
|
||||
if self.conf_session_timing_expanded:
|
||||
line = table.row()
|
||||
line.label(text=" ")
|
||||
@@ -224,7 +253,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
line.label(text="apply (sec)")
|
||||
|
||||
for item in self.supported_datablocks:
|
||||
line = table.row(align=True)
|
||||
line = table.row(align=True)
|
||||
line.label(text="", icon=item.icon)
|
||||
line.prop(item, "bl_delay_refresh", text="")
|
||||
line.prop(item, "bl_delay_apply", text="")
|
||||
@@ -239,7 +268,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
row = box.row()
|
||||
row.label(text="Start with an empty scene:")
|
||||
row.prop(self, "start_empty", text="")
|
||||
|
||||
|
||||
# CACHE SETTINGS
|
||||
box = grid.box()
|
||||
box.prop(
|
||||
@@ -249,6 +278,15 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
if self.conf_session_cache_expanded:
|
||||
box.row().prop(self, "cache_directory", text="Cache directory")
|
||||
|
||||
# INTERFACE SETTINGS
|
||||
box = grid.box()
|
||||
box.prop(
|
||||
self, "conf_session_ui_expanded", text="Interface",
|
||||
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_ui_expanded else 'DISCLOSURE_TRI_RIGHT',
|
||||
emboss=False)
|
||||
if self.conf_session_ui_expanded:
|
||||
box.row().prop(self, "panel_category", text="Panel category", expand=True)
|
||||
|
||||
if self.category == 'UPDATE':
|
||||
from . import addon_updater_ops
|
||||
addon_updater_ops.update_settings_ui_condensed(self, context)
|
||||
@@ -260,7 +298,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
new_db = self.supported_datablocks.add()
|
||||
|
||||
type_module = getattr(bl_types, type)
|
||||
type_impl_name = "Bl{}".format(type.split('_')[1].capitalize())
|
||||
type_impl_name = f"Bl{type.split('_')[1].capitalize()}"
|
||||
type_module_class = getattr(type_module, type_impl_name)
|
||||
|
||||
new_db.name = type_impl_name
|
||||
@@ -275,7 +313,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
|
||||
def client_list_callback(scene, context):
|
||||
from . import operators
|
||||
|
||||
|
||||
items = [(RP_COMMON, RP_COMMON, "")]
|
||||
|
||||
username = utils.get_preferences().username
|
||||
@@ -283,11 +321,11 @@ def client_list_callback(scene, context):
|
||||
if cli:
|
||||
client_ids = cli.online_users.keys()
|
||||
for id in client_ids:
|
||||
name_desc = id
|
||||
if id == username:
|
||||
name_desc += " (self)"
|
||||
name_desc = id
|
||||
if id == username:
|
||||
name_desc += " (self)"
|
||||
|
||||
items.append((id, name_desc, ""))
|
||||
items.append((id, name_desc, ""))
|
||||
|
||||
return items
|
||||
|
||||
@@ -305,7 +343,7 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
is_admin: bpy.props.BoolProperty(
|
||||
name="is_admin",
|
||||
default=False
|
||||
)
|
||||
)
|
||||
session_mode: bpy.props.EnumProperty(
|
||||
name='session_mode',
|
||||
description='session mode',
|
||||
@@ -322,7 +360,7 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
description='Enable overlay drawing module',
|
||||
default=True,
|
||||
update=presence.update_presence
|
||||
)
|
||||
)
|
||||
presence_show_selected: bpy.props.BoolProperty(
|
||||
name="Show selected objects",
|
||||
description='Enable selection overlay ',
|
||||
@@ -334,13 +372,13 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
description='Enable user overlay ',
|
||||
default=True,
|
||||
update=presence.update_overlay_settings
|
||||
)
|
||||
)
|
||||
presence_show_far_user: bpy.props.BoolProperty(
|
||||
name="Show different scenes",
|
||||
description="Show user on different scenes",
|
||||
default=False,
|
||||
update=presence.update_overlay_settings
|
||||
)
|
||||
)
|
||||
filter_owned: bpy.props.BoolProperty(
|
||||
name="filter_owned",
|
||||
description='Show only owned datablocks',
|
||||
@@ -353,6 +391,7 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
default=False
|
||||
)
|
||||
|
||||
|
||||
classes = (
|
||||
SessionUser,
|
||||
SessionProps,
|
||||
@@ -370,7 +409,7 @@ def register():
|
||||
|
||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||
if len(prefs.supported_datablocks) == 0:
|
||||
logger.info('Generating bl_types preferences')
|
||||
logging.debug('Generating bl_types preferences')
|
||||
prefs.generate_supported_types()
|
||||
|
||||
|
||||
|
||||
+17
-8
@@ -32,10 +32,12 @@ from . import utils
|
||||
|
||||
renderer = None
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def view3d_find():
|
||||
""" Find the first 'VIEW_3D' windows found in areas
|
||||
|
||||
:return: tuple(Area, Region, RegionView3D)
|
||||
"""
|
||||
for area in bpy.data.window_managers[0].windows[0].screen.areas:
|
||||
if area.type == 'VIEW_3D':
|
||||
v3d = area.spaces[0]
|
||||
@@ -43,15 +45,22 @@ def view3d_find():
|
||||
for region in area.regions:
|
||||
if region.type == 'WINDOW':
|
||||
return area, region, rv3d
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def refresh_3d_view():
|
||||
""" Refresh the viewport
|
||||
"""
|
||||
area, region, rv3d = view3d_find()
|
||||
if area and region and rv3d:
|
||||
area.tag_redraw()
|
||||
|
||||
def refresh_sidebar_view():
|
||||
""" Refresh the blender sidebar
|
||||
"""
|
||||
area, region, rv3d = view3d_find()
|
||||
|
||||
area.regions[3].tag_redraw()
|
||||
|
||||
def get_target(region, rv3d, coord):
|
||||
target = [0, 0, 0]
|
||||
@@ -199,7 +208,7 @@ class DrawFactory(object):
|
||||
|
||||
def flush_selection(self, user=None):
|
||||
key_to_remove = []
|
||||
select_key = "{}_select".format(user) if user else "select"
|
||||
select_key = f"{user}_select" if user else "select"
|
||||
for k in self.d3d_items.keys():
|
||||
|
||||
if select_key in k:
|
||||
@@ -226,7 +235,7 @@ class DrawFactory(object):
|
||||
self.flush_selection(client_id)
|
||||
|
||||
for select_ob in client_selection:
|
||||
drawable_key = "{}_select_{}".format(client_id, select_ob)
|
||||
drawable_key = f"{client_id}_select_{select_ob}"
|
||||
|
||||
ob = utils.find_from_attr("uuid", select_ob, bpy.data.objects)
|
||||
if not ob:
|
||||
@@ -302,7 +311,7 @@ class DrawFactory(object):
|
||||
self.d2d_items[client_id] = (position[1], client_id, color)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Draw client exception {}".format(e))
|
||||
logging.error(f"Draw client exception: {e}")
|
||||
|
||||
def draw3d_callback(self):
|
||||
bgl.glLineWidth(1.5)
|
||||
@@ -316,7 +325,7 @@ class DrawFactory(object):
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
except Exception:
|
||||
logger.error("3D Exception")
|
||||
logging.error("3D Exception")
|
||||
|
||||
def draw2d_callback(self):
|
||||
for position, font, color in self.d2d_items.values():
|
||||
@@ -330,7 +339,7 @@ class DrawFactory(object):
|
||||
blf.draw(0, font)
|
||||
|
||||
except Exception:
|
||||
logger.error("2D EXCEPTION")
|
||||
logging.error("2D EXCEPTION")
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
+6
-13
@@ -48,10 +48,9 @@ def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1,
|
||||
From here:
|
||||
https://gist.github.com/greenstick/b23e475d2bfdc3a82e34eaa1f6781ee4
|
||||
"""
|
||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||
filledLength = int(length * iteration // total)
|
||||
bar = fill * filledLength + fill_empty * (length - filledLength)
|
||||
return '{} |{}| {}/{}{}'.format(prefix, bar, iteration,total, suffix)
|
||||
return f"{prefix} |{bar}| {iteration}/{total}{suffix}"
|
||||
|
||||
def get_state_str(state):
|
||||
state_str = 'UNKNOWN'
|
||||
@@ -150,7 +149,6 @@ class SESSION_PT_settings_network(bpy.types.Panel):
|
||||
bl_label = "Network"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
|
||||
@classmethod
|
||||
@@ -199,7 +197,6 @@ class SESSION_PT_settings_user(bpy.types.Panel):
|
||||
bl_label = "User"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
|
||||
@classmethod
|
||||
@@ -227,7 +224,6 @@ class SESSION_PT_settings_replication(bpy.types.Panel):
|
||||
bl_label = "Advanced"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@@ -276,7 +272,6 @@ class SESSION_PT_user(bpy.types.Panel):
|
||||
bl_label = "Online users"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
|
||||
@classmethod
|
||||
@@ -355,7 +350,6 @@ class SESSION_PT_presence(bpy.types.Panel):
|
||||
bl_label = "Presence overlay"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@@ -385,7 +379,6 @@ class SESSION_PT_services(bpy.types.Panel):
|
||||
bl_label = "Services"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@@ -404,8 +397,7 @@ class SESSION_PT_services(bpy.types.Panel):
|
||||
for name, state in operators.client.services_state.items():
|
||||
row = layout.row()
|
||||
row.label(text=name)
|
||||
row.label(text=get_state_str(state))
|
||||
|
||||
row.label(text=get_state_str(state))
|
||||
|
||||
|
||||
|
||||
@@ -429,7 +421,7 @@ def draw_property(context, parent, property_uuid, level=0):
|
||||
|
||||
detail_item_box.label(text="",
|
||||
icon=settings.supported_datablocks[item.str_type].icon)
|
||||
detail_item_box.label(text="{} ".format(name))
|
||||
detail_item_box.label(text=f"{name}")
|
||||
|
||||
# Operations
|
||||
|
||||
@@ -477,7 +469,7 @@ class SESSION_PT_outliner(bpy.types.Panel):
|
||||
bl_label = "Properties"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -537,8 +529,9 @@ classes = (
|
||||
SESSION_PT_presence,
|
||||
SESSION_PT_settings_replication,
|
||||
SESSION_PT_user,
|
||||
SESSION_PT_services,
|
||||
SESSION_PT_outliner,
|
||||
SESSION_PT_services
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@ import mathutils
|
||||
|
||||
from . import environment, presence
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def find_from_attr(attr_name, attr_value, list):
|
||||
for item in list:
|
||||
|
||||
Reference in New Issue
Block a user