Compare commits

..

16 Commits

Author SHA1 Message Date
ab350ca7bc fix: late update logging error
Related to #208
2021-06-24 17:24:08 +02:00
de73f022e6 merge 2021-06-24 14:52:07 +02:00
f517205647 fix: doc authors 2021-06-24 14:51:00 +02:00
f33c3d8481 fix: doc version 2021-06-24 14:50:12 +02:00
71c69000ec Merge branch '207-repository-panel-filtering-is-boken' into 'develop'
Resolve "Repository panel filtering is boken"

See merge request slumber/multi-user!132
2021-06-24 12:49:06 +00:00
de1e684b3c fix: name filtering 2021-06-24 14:35:59 +02:00
d87730cffb Merge branch '197-user-selection-bounding-box-glitches-for-non-mesh-objects' into 'develop'
User selection bounding box glitches for non-mesh objects

See merge request slumber/multi-user!129
2021-06-23 16:02:50 +00:00
3f005b86ab fix : add enumerate / remove nb_object 2021-06-23 17:45:01 +02:00
5098e5135d fix: bbox work for non-mesh objects+ins.collection 2021-06-23 17:00:05 +02:00
37cfed489c Merge branch '204-animation-doesn-t-sync-for-gpencil-materials' into 'develop'
Resolve "Animation doesn't sync for materials"

See merge request slumber/multi-user!128
2021-06-22 12:10:23 +00:00
9003abcd18 feat: notes for furtur improvements 2021-06-22 14:06:19 +02:00
a199e0df00 feat: apply bl_apply_child member to force dependencies reloading
fix: node_tree animation dependencies
2021-06-22 11:36:51 +02:00
3774419b7e fix: force push is now pushing the whole node data instead of delta 2021-06-22 10:41:36 +02:00
3e552cb406 feat: gpencil materials animation support 2021-06-22 10:39:40 +02:00
9f381b44c8 fix: material animation support 2021-06-21 18:58:16 +02:00
ad795caed5 fix: only apply repository heads on connection 2021-06-21 18:38:43 +02:00
8 changed files with 91 additions and 56 deletions

View File

@ -19,10 +19,10 @@ import sys
project = 'multi-user' project = 'multi-user'
copyright = '2020, Swann Martinez' copyright = '2020, Swann Martinez'
author = 'Swann Martinez, with contributions from Poochy' author = 'Swann Martinez, Poochy, Fabian'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = '0.2.0' release = '0.5.0-develop'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View File

@ -219,7 +219,7 @@ def load_fcurve(fcurve_data, fcurve):
def dump_animation_data(datablock): def dump_animation_data(datablock):
animation_data = {} animation_data = {}
if has_action(datablock): if has_action(datablock):
animation_data['action'] = datablock.animation_data.action.name animation_data['action'] = datablock.animation_data.action.uuid
if has_driver(datablock): if has_driver(datablock):
animation_data['drivers'] = [] animation_data['drivers'] = []
for driver in datablock.animation_data.drivers: for driver in datablock.animation_data.drivers:
@ -241,8 +241,10 @@ def load_animation_data(animation_data, datablock):
for driver in animation_data['drivers']: for driver in animation_data['drivers']:
load_driver(datablock, driver) load_driver(datablock, driver)
if 'action' in animation_data: action = animation_data.get('action')
datablock.animation_data.action = bpy.data.actions[animation_data['action']] if action:
action = resolve_datablock_from_uuid(action, bpy.data.actions)
datablock.animation_data.action = action
elif datablock.animation_data.action: elif datablock.animation_data.action:
datablock.animation_data.action = None datablock.animation_data.action = None

View File

@ -404,6 +404,7 @@ class BlMaterial(ReplicatedDatablock):
bl_check_common = False bl_check_common = False
bl_icon = 'MATERIAL_DATA' bl_icon = 'MATERIAL_DATA'
bl_reload_parent = False bl_reload_parent = False
bl_reload_child = True
@staticmethod @staticmethod
def construct(data: dict) -> object: def construct(data: dict) -> object:
@ -411,8 +412,6 @@ class BlMaterial(ReplicatedDatablock):
@staticmethod @staticmethod
def load(data: dict, datablock: object): def load(data: dict, datablock: object):
load_animation_data(data.get('animation_data'), datablock)
loader = Loader() loader = Loader()
is_grease_pencil = data.get('is_grease_pencil') is_grease_pencil = data.get('is_grease_pencil')
@ -429,6 +428,8 @@ class BlMaterial(ReplicatedDatablock):
datablock.use_nodes = True datablock.use_nodes = True
load_node_tree(data['node_tree'], datablock.node_tree) load_node_tree(data['node_tree'], datablock.node_tree)
load_animation_data(data.get('nodes_animation_data'), datablock.node_tree)
load_animation_data(data.get('animation_data'), datablock)
@staticmethod @staticmethod
def dump(datablock: object) -> dict: def dump(datablock: object) -> dict:
@ -496,8 +497,10 @@ class BlMaterial(ReplicatedDatablock):
data['grease_pencil'] = gp_mat_dumper.dump(datablock.grease_pencil) data['grease_pencil'] = gp_mat_dumper.dump(datablock.grease_pencil)
elif datablock.use_nodes: elif datablock.use_nodes:
data['node_tree'] = dump_node_tree(datablock.node_tree) data['node_tree'] = dump_node_tree(datablock.node_tree)
data['nodes_animation_data'] = dump_animation_data(datablock.node_tree)
data['animation_data'] = dump_animation_data(datablock) data['animation_data'] = dump_animation_data(datablock)
return data return data
@staticmethod @staticmethod
@ -511,7 +514,7 @@ class BlMaterial(ReplicatedDatablock):
if datablock.use_nodes: if datablock.use_nodes:
deps.extend(get_node_tree_dependencies(datablock.node_tree)) deps.extend(get_node_tree_dependencies(datablock.node_tree))
deps.extend(resolve_animation_dependencies(datablock.node_tree))
deps.extend(resolve_animation_dependencies(datablock)) deps.extend(resolve_animation_dependencies(datablock))
return deps return deps

View File

@ -100,7 +100,7 @@ def initialize_session():
# Step 2: Load nodes # Step 2: Load nodes
logging.info("Applying nodes") logging.info("Applying nodes")
for node in session.repository.index_sorted: for node in session.repository.heads:
porcelain.apply(session.repository, node) porcelain.apply(session.repository, node)
logging.info("Registering timers") logging.info("Registering timers")
@ -604,9 +604,9 @@ class SessionApply(bpy.types.Operator):
node_ref = session.repository.graph.get(self.target) node_ref = session.repository.graph.get(self.target)
porcelain.apply(session.repository, porcelain.apply(session.repository,
self.target, self.target,
force=True, force=True)
force_dependencies=self.reset_dependencies)
impl = session.repository.rdp.get_implementation(node_ref.instance) impl = session.repository.rdp.get_implementation(node_ref.instance)
# NOTE: find another way to handle child and parent automatic reloading
if impl.bl_reload_parent: if impl.bl_reload_parent:
for parent in session.repository.graph.get_parents(self.target): for parent in session.repository.graph.get_parents(self.target):
logging.debug(f"Refresh parent {parent}") logging.debug(f"Refresh parent {parent}")
@ -614,6 +614,11 @@ class SessionApply(bpy.types.Operator):
porcelain.apply(session.repository, porcelain.apply(session.repository,
parent.uuid, parent.uuid,
force=True) force=True)
if hasattr(impl, 'bl_reload_child') and impl.bl_reload_child:
for dep in node_ref.dependencies:
porcelain.apply(session.repository,
dep,
force=True)
except Exception as e: except Exception as e:
self.report({'ERROR'}, repr(e)) self.report({'ERROR'}, repr(e))
traceback.print_exc() traceback.print_exc()
@ -637,7 +642,7 @@ class SessionCommit(bpy.types.Operator):
def execute(self, context): def execute(self, context):
try: try:
porcelain.commit(session.repository, self.target) porcelain.commit(session.repository, self.target)
porcelain.push(session.repository, 'origin', self.target) porcelain.push(session.repository, 'origin', self.target, force=True)
return {"FINISHED"} return {"FINISHED"}
except Exception as e: except Exception as e:
self.report({'ERROR'}, repr(e)) self.report({'ERROR'}, repr(e))

View File

@ -94,15 +94,35 @@ def project_to_viewport(region: bpy.types.Region, rv3d: bpy.types.RegionView3D,
return [target.x, target.y, target.z] return [target.x, target.y, target.z]
def bbox_from_obj(obj: bpy.types.Object, radius: float) -> list: def bbox_from_obj(obj: bpy.types.Object) -> list:
""" Generate a bounding box for a given object by using its world matrix """ Generate a bounding box for a given object by using its world matrix
:param obj: target object :param obj: target object
:type obj: bpy.types.Object :type obj: bpy.types.Object
:param radius: bounding box radius :return: list of 8 points [(x,y,z),...], list of 12 link between these points [(1,2),...]
:type radius: float
:return: list of 8 points [(x,y,z),...]
""" """
radius = 1.0 # Radius of the bounding box
vertex_indices = (
(0, 1), (0, 2), (1, 3), (2, 3),
(4, 5), (4, 6), (5, 7), (6, 7),
(0, 4), (1, 5), (2, 6), (3, 7))
if obj.type == 'EMPTY':
radius = obj.empty_display_size
elif obj.type == 'LIGHT':
radius = obj.data.shadow_soft_size
elif obj.type == 'LIGHT_PROBE':
radius = obj.data.influence_distance
elif obj.type == 'CAMERA':
radius = obj.data.display_size
elif hasattr(obj, 'bound_box'):
vertex_indices = (
(0, 1), (1, 2), (2, 3), (0, 3),
(4, 5), (5, 6), (6, 7), (4, 7),
(0, 4), (1, 5), (2, 6), (3, 7))
vertex_pos = get_bb_coords_from_obj(obj)
return vertex_pos, vertex_indices
coords = [ coords = [
(-radius, -radius, -radius), (+radius, -radius, -radius), (-radius, -radius, -radius), (+radius, -radius, -radius),
(-radius, +radius, -radius), (+radius, +radius, -radius), (-radius, +radius, -radius), (+radius, +radius, -radius),
@ -112,9 +132,37 @@ def bbox_from_obj(obj: bpy.types.Object, radius: float) -> list:
base = obj.matrix_world base = obj.matrix_world
bbox_corners = [base @ mathutils.Vector(corner) for corner in coords] bbox_corners = [base @ mathutils.Vector(corner) for corner in coords]
return [(point.x, point.y, point.z) vertex_pos = [(point.x, point.y, point.z) for point in bbox_corners]
for point in bbox_corners]
return vertex_pos, vertex_indices
def bbox_from_instance_collection(ic: bpy.types.Object) -> list:
""" Generate a bounding box for a given instance collection by using its objects
:param ic: target instance collection
:type ic: bpy.types.Object
:param radius: bounding box radius
:type radius: float
:return: list of 8*objs points [(x,y,z),...], tuple of 12*objs link between these points [(1,2),...]
"""
vertex_pos = []
vertex_indices = ()
for obj_index, obj in enumerate(ic.instance_collection.objects):
vertex_pos_temp, vertex_indices_temp = bbox_from_obj(obj)
vertex_pos += vertex_pos_temp
vertex_indices_list_temp = list(list(indice) for indice in vertex_indices_temp)
for indice in vertex_indices_list_temp:
indice[0] += 8*obj_index
indice[1] += 8*obj_index
vertex_indices_temp = tuple(tuple(indice) for indice in vertex_indices_list_temp)
vertex_indices += vertex_indices_temp
bbox_corners = [ic.matrix_world @ mathutils.Vector(vertex) for vertex in vertex_pos]
vertex_pos = [(point.x, point.y, point.z) for point in bbox_corners]
return vertex_pos, vertex_indices
def generate_user_camera() -> list: def generate_user_camera() -> list:
""" Generate a basic camera represention of the user point of view """ Generate a basic camera represention of the user point of view
@ -296,36 +344,14 @@ class UserSelectionWidget(Widget):
def draw(self): def draw(self):
user_selection = self.data.get('selected_objects') user_selection = self.data.get('selected_objects')
for select_ob in user_selection: for select_obj in user_selection:
ob = find_from_attr("uuid", select_ob, bpy.data.objects) obj = find_from_attr("uuid", select_obj, bpy.data.objects)
if not ob: if not obj:
return return
if obj.instance_collection:
vertex_pos = bbox_from_obj(ob, 1.0) vertex_pos, vertex_indices = bbox_from_instance_collection(obj)
vertex_indices = ( else :
(0, 1), (1, 2), (2, 3), (0, 3), vertex_pos, vertex_indices = bbox_from_obj(obj)
(4, 5), (5, 6), (6, 7), (4, 7),
(0, 4), (1, 5), (2, 6), (3, 7))
if ob.instance_collection:
for obj in ob.instance_collection.objects:
if obj.type == 'MESH' and hasattr(obj, 'bound_box'):
vertex_pos = get_bb_coords_from_obj(obj, instance=ob)
break
elif ob.type == 'EMPTY':
vertex_pos = bbox_from_obj(ob, ob.empty_display_size)
elif ob.type == 'LIGHT':
vertex_pos = bbox_from_obj(ob, ob.data.shadow_soft_size)
elif ob.type == 'LIGHT_PROBE':
vertex_pos = bbox_from_obj(ob, ob.data.influence_distance)
elif ob.type == 'CAMERA':
vertex_pos = bbox_from_obj(ob, ob.data.display_size)
elif hasattr(ob, 'bound_box'):
vertex_indices = (
(0, 1), (1, 2), (2, 3), (0, 3),
(4, 5), (5, 6), (6, 7), (4, 7),
(0, 4), (1, 5), (2, 6), (3, 7))
vertex_pos = get_bb_coords_from_obj(ob)
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR') shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
batch = batch_for_shader( batch = batch_for_shader(
@ -338,7 +364,6 @@ class UserSelectionWidget(Widget):
shader.uniform_float("color", self.data.get('color')) shader.uniform_float("color", self.data.get('color'))
batch.draw(shader) batch.draw(shader)
class UserNameWidget(Widget): class UserNameWidget(Widget):
draw_type = 'POST_PIXEL' draw_type = 'POST_PIXEL'

View File

@ -129,6 +129,11 @@ class ApplyTimer(Timer):
porcelain.apply(session.repository, porcelain.apply(session.repository,
parent.uuid, parent.uuid,
force=True) force=True)
if hasattr(impl, 'bl_reload_child') and impl.bl_reload_child:
for dep in node_ref.dependencies:
porcelain.apply(session.repository,
dep,
force=True)
class DynamicRightSelectTimer(Timer): class DynamicRightSelectTimer(Timer):

View File

@ -555,20 +555,15 @@ class SESSION_PT_repository(bpy.types.Panel):
# Properties # Properties
owned_nodes = [k for k, v in session.repository.graph.items() if v.owner==settings.username] owned_nodes = [k for k, v in session.repository.graph.items() if v.owner==settings.username]
filtered_node = owned_nodes if runtime_settings.filter_owned else session.repository.graph.keys() filtered_node = owned_nodes if runtime_settings.filter_owned else list(session.repository.graph.keys())
if runtime_settings.filter_name: if runtime_settings.filter_name:
for node_id in filtered_node: filtered_node = [n for n in filtered_node if runtime_settings.filter_name.lower() in session.repository.graph.get(n).data.get('name').lower()]
node_instance = session.repository.graph.get(node_id)
name = node_instance.data.get('name')
if runtime_settings.filter_name not in name:
filtered_node.remove(node_id)
if filtered_node: if filtered_node:
col = layout.column(align=True) col = layout.column(align=True)
for key in filtered_node: for key in filtered_node:
draw_property(context, col, key) draw_property(context, col, key)
else: else:
layout.row().label(text="Empty") layout.row().label(text="Empty")