Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d78c42b02f | |||
| 8cb40b2d60 | |||
| 57fdd492ef |
+1
-2
@@ -13,5 +13,4 @@ multi_user_updater/
|
||||
_build
|
||||
|
||||
# ignore generated zip generated from blender_addon_tester
|
||||
*.zip
|
||||
libs
|
||||
*.zip
|
||||
@@ -8,5 +8,3 @@ build:
|
||||
name: multi_user
|
||||
paths:
|
||||
- multi_user
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
@@ -5,7 +5,6 @@ deploy:
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
services:
|
||||
- docker:19.03.12-dind
|
||||
|
||||
@@ -3,5 +3,3 @@ test:
|
||||
image: slumber/blender-addon-testing:latest
|
||||
script:
|
||||
- python3 scripts/test_addon.py
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "multi_user/libs/replication"]
|
||||
path = multi_user/libs/replication
|
||||
url = https://gitlab.com/slumber/replication.git
|
||||
|
||||
@@ -32,32 +32,32 @@ Currently, not all data-block are supported for replication over the wire. The f
|
||||
| Name | Status | Comment |
|
||||
| -------------- | :----: | :----------------------------------------------------------: |
|
||||
| action | ✔️ | |
|
||||
| armature | ❗ | Not stable |
|
||||
| camera | ✔️ | |
|
||||
| collection | ✔️ | |
|
||||
| curve | ❗ | Nurbs surfaces not supported |
|
||||
| gpencil | ✔️ | |
|
||||
| image | ✔️ | |
|
||||
| mesh | ✔️ | |
|
||||
| material | ✔️ | |
|
||||
| node_groups | ✔️ | Material & Geometry only |
|
||||
| node_groups | ❗ | Material & Geometry only |
|
||||
| geometry nodes | ✔️ | |
|
||||
| metaball | ✔️ | |
|
||||
| object | ✔️ | |
|
||||
| textures | ❗ | Supported for modifiers/materials/geo nodes only |
|
||||
| texts | ✔️ | |
|
||||
| scene | ✔️ | |
|
||||
| world | ✔️ | |
|
||||
| volumes | ✔️ | |
|
||||
| lightprobes | ✔️ | |
|
||||
| physics | ✔️ | |
|
||||
| curve | ❗ | Nurbs surfaces not supported |
|
||||
| textures | ❗ | Supported for modifiers/materials/geo nodes only |
|
||||
| armature | ❗ | Not stable |
|
||||
| compositing | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/46) |
|
||||
| texts | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/81) |
|
||||
| nla | ❌ | |
|
||||
| volumes | ✔️ | |
|
||||
| particles | ❗ | The cache isn't syncing. |
|
||||
| speakers | ❗ | [Partial](https://gitlab.com/slumber/multi-user/-/issues/65) |
|
||||
| vse | ❗ | Mask and Clip not supported yet |
|
||||
| physics | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/45) |
|
||||
| libraries | ❗ | Partial |
|
||||
| nla | ❌ | |
|
||||
| texts | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/81) |
|
||||
| compositing | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/46) |
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 320 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -108,69 +108,36 @@ Before starting make sure that you have access to the session IP address and por
|
||||
1. Fill in your user information
|
||||
--------------------------------
|
||||
|
||||
Joining a server
|
||||
=======================
|
||||
Follow the user-info_ section for this step.
|
||||
|
||||
--------------
|
||||
Network setup
|
||||
--------------
|
||||
----------------
|
||||
2. Network setup
|
||||
----------------
|
||||
|
||||
In the network panel, select **JOIN**.
|
||||
The **join sub-panel** (see image below) allows you to configure your client to join a
|
||||
collaborative session which is already hosted.
|
||||
|
||||
.. figure:: img/server_preset_image_normal_server.png
|
||||
:align: center
|
||||
:width: 200px
|
||||
.. figure:: img/quickstart_join.png
|
||||
:align: center
|
||||
:alt: Connect menu
|
||||
|
||||
Connection pannel
|
||||
Connection panel
|
||||
|
||||
Fill in the fields with your information:
|
||||
|
||||
- **IP**: the host's IP address.
|
||||
- **Port**: the host's port number.
|
||||
- **Connect as admin**: connect yourself with **admin rights** (see :ref:`admin` ) to the session.
|
||||
|
||||
Once you've configured every field, hit the button **CONNECT** to join the session !
|
||||
When the :ref:`session-status` is **ONLINE** you are online and ready to start co-creating.
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to have **administrator rights** (see :ref:`admin` ) on the server, just enter the password created by the host in the **Connect as admin** section
|
||||
|
||||
.. figure:: img/server_preset_image_admin.png
|
||||
:align: center
|
||||
:width: 200px
|
||||
|
||||
Admin password
|
||||
|
||||
---------------
|
||||
Server presets
|
||||
---------------
|
||||
|
||||
You can save your server presets in a preset list below the 'JOIN' and 'HOST' buttons. This allows you to quickly access and manage your servers.
|
||||
|
||||
To add a server, first enter the ip address and the port (plus the password if needed), then click on the + icon to add a name to your preset. To remove a server from the list, select it and click on the - icon.
|
||||
|
||||
.. figure:: img/server_preset_exemple.gif
|
||||
:align: center
|
||||
:width: 200px
|
||||
|
||||
.. warning:: Be careful, if you don't rename your new preset, or if it has the same name as an existing preset, the old preset will be overwritten.
|
||||
|
||||
.. figure:: img/server_preset_image_report.png
|
||||
:align: center
|
||||
:width: 200px
|
||||
|
||||
.. note::
|
||||
|
||||
Two presets are already present when the addon is launched:
|
||||
|
||||
- The 'localhost' preset, to host and join a local session quickly
|
||||
- The 'public session' preset, to join the public sessions of the multi-user server (official discord to participate : https://discord.gg/aBPvGws)
|
||||
.. Maybe something more explicit here
|
||||
|
||||
.. note::
|
||||
Additional configuration settings can be found in the :ref:`advanced` section.
|
||||
|
||||
Once you've configured every field, hit the button **CONNECT** to join the session !
|
||||
When the :ref:`session-status` is **ONLINE** you are online and ready to start co-creating.
|
||||
|
||||
.. note::
|
||||
When starting a **dedicated server**, the session status screen will take you to the **LOBBY**, awaiting an admin to start the session.
|
||||
|
||||
@@ -407,6 +374,15 @@ Network
|
||||
|
||||
Advanced network settings
|
||||
|
||||
**IPC Port** is the port used for Inter Process Communication. This port is used
|
||||
by the multi-user subprocesses to communicate with each other. If different instances
|
||||
of multi-user are using the same IPC port, this will create conflict !
|
||||
|
||||
.. note::
|
||||
You only need to modify this setting if you need to launch multiple clients from the same
|
||||
computer (or if you try to host and join from the same computer). To resolve this, you simply need to enter a different
|
||||
**IPC port** for each blender instance.
|
||||
|
||||
**Timeout (in milliseconds)** is the maximum ping authorized before auto-disconnecting.
|
||||
You should only increase it if you have a bad connection.
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ Hit 'Create a network'(see image below) and go to the network settings.
|
||||
:align: center
|
||||
:width: 450px
|
||||
|
||||
Admin password
|
||||
Network page
|
||||
|
||||
Now that the network is created, let's configure it.
|
||||
|
||||
|
||||
+12
-6
@@ -19,7 +19,7 @@
|
||||
bl_info = {
|
||||
"name": "Multi-User",
|
||||
"author": "Swann Martinez",
|
||||
"version": (0, 5, 0),
|
||||
"version": (0, 3, 0),
|
||||
"description": "Enable real-time collaborative workflow inside blender",
|
||||
"blender": (2, 82, 0),
|
||||
"location": "3D View > Sidebar > Multi-User tab",
|
||||
@@ -43,10 +43,13 @@ from bpy.app.handlers import persistent
|
||||
from . import environment
|
||||
|
||||
|
||||
DEPENDENCIES = {
|
||||
("replication", '0.1.26'),
|
||||
}
|
||||
|
||||
|
||||
module_error_msg = "Insufficient rights to install the multi-user \
|
||||
dependencies, aunch blender with administrator rights."
|
||||
|
||||
|
||||
def register():
|
||||
# Setup logging policy
|
||||
logging.basicConfig(
|
||||
@@ -55,7 +58,12 @@ def register():
|
||||
level=logging.INFO)
|
||||
|
||||
try:
|
||||
environment.register()
|
||||
if bpy.app.version[1] >= 91:
|
||||
python_binary_path = sys.executable
|
||||
else:
|
||||
python_binary_path = bpy.app.binary_path_python
|
||||
|
||||
environment.setup(DEPENDENCIES, python_binary_path)
|
||||
|
||||
from . import presence
|
||||
from . import operators
|
||||
@@ -103,5 +111,3 @@ def unregister():
|
||||
del bpy.types.ID.uuid
|
||||
del bpy.types.WindowManager.online_users
|
||||
del bpy.types.WindowManager.user_index
|
||||
|
||||
environment.unregister()
|
||||
|
||||
@@ -1688,7 +1688,10 @@ class GitlabEngine(object):
|
||||
# Could clash with tag names and if it does, it will
|
||||
# download TAG zip instead of branch zip to get
|
||||
# direct path, would need.
|
||||
return f"https://gitlab.com/slumber/multi-user/-/jobs/artifacts/{branch}/download?job=build"
|
||||
return "{}{}{}".format(
|
||||
self.form_repo_url(updater),
|
||||
"/repository/archive.zip?sha=",
|
||||
branch)
|
||||
|
||||
def get_zip_url(self, sha, updater):
|
||||
return "{base}/repository/archive.zip?sha={sha}".format(
|
||||
|
||||
@@ -28,6 +28,7 @@ __all__ = [
|
||||
'bl_light',
|
||||
'bl_scene',
|
||||
'bl_material',
|
||||
'bl_library',
|
||||
'bl_armature',
|
||||
'bl_action',
|
||||
'bl_world',
|
||||
@@ -38,6 +39,7 @@ __all__ = [
|
||||
'bl_font',
|
||||
'bl_sound',
|
||||
'bl_file',
|
||||
# 'bl_sequencer',
|
||||
'bl_node_group',
|
||||
'bl_texture',
|
||||
"bl_particle",
|
||||
@@ -47,18 +49,8 @@ if bpy.app.version[1] >= 91:
|
||||
__all__.append('bl_volume')
|
||||
|
||||
from . import *
|
||||
from replication.data import ReplicatedDataFactory
|
||||
|
||||
def types_to_register():
|
||||
return __all__
|
||||
|
||||
from replication.protocol import DataTranslationProtocol
|
||||
|
||||
def get_data_translation_protocol()-> DataTranslationProtocol:
|
||||
""" Return a data translation protocol from implemented bpy types
|
||||
"""
|
||||
bpy_protocol = DataTranslationProtocol()
|
||||
for module_name in __all__:
|
||||
impl = globals().get(module_name)
|
||||
if impl and hasattr(impl, "_type") and hasattr(impl, "_type"):
|
||||
bpy_protocol.register_implementation(impl._type, impl._class)
|
||||
return bpy_protocol
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,9 +22,8 @@ import mathutils
|
||||
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .. import presence, operators, utils
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from .bl_datablock import resolve_datablock_from_uuid
|
||||
from .bl_action import dump_animation_data, load_animation_data, resolve_animation_dependencies
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
|
||||
def get_roll(bone: bpy.types.Bone) -> float:
|
||||
""" Compute the actuall roll of a pose bone
|
||||
@@ -36,19 +35,17 @@ def get_roll(bone: bpy.types.Bone) -> float:
|
||||
return bone.AxisRollFromMatrix(bone.matrix_local.to_3x3())[1]
|
||||
|
||||
|
||||
class BlArmature(ReplicatedDatablock):
|
||||
class BlArmature(BlDatablock):
|
||||
bl_id = "armatures"
|
||||
bl_class = bpy.types.Armature
|
||||
bl_check_common = False
|
||||
bl_icon = 'ARMATURE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
def _construct(self, data):
|
||||
return bpy.data.armatures.new(data["name"])
|
||||
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
def _load_implementation(self, data, target):
|
||||
# Load parent object
|
||||
parent_object = utils.find_from_attr(
|
||||
'uuid',
|
||||
@@ -58,7 +55,7 @@ class BlArmature(ReplicatedDatablock):
|
||||
|
||||
if parent_object is None:
|
||||
parent_object = bpy.data.objects.new(
|
||||
data['user_name'], datablock)
|
||||
data['user_name'], target)
|
||||
parent_object.uuid = data['user']
|
||||
|
||||
is_object_in_master = (
|
||||
@@ -93,10 +90,10 @@ class BlArmature(ReplicatedDatablock):
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
for bone in data['bones']:
|
||||
if bone not in datablock.edit_bones:
|
||||
new_bone = datablock.edit_bones.new(bone)
|
||||
if bone not in target.edit_bones:
|
||||
new_bone = target.edit_bones.new(bone)
|
||||
else:
|
||||
new_bone = datablock.edit_bones[bone]
|
||||
new_bone = target.edit_bones[bone]
|
||||
|
||||
bone_data = data['bones'].get(bone)
|
||||
|
||||
@@ -107,7 +104,7 @@ class BlArmature(ReplicatedDatablock):
|
||||
new_bone.roll = bone_data['roll']
|
||||
|
||||
if 'parent' in bone_data:
|
||||
new_bone.parent = datablock.edit_bones[data['bones']
|
||||
new_bone.parent = target.edit_bones[data['bones']
|
||||
[bone]['parent']]
|
||||
new_bone.use_connect = bone_data['use_connect']
|
||||
|
||||
@@ -122,10 +119,9 @@ class BlArmature(ReplicatedDatablock):
|
||||
if 'EDIT' in current_mode:
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
load_animation_data(data.get('animation_data'), datablock)
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
dumper = Dumper()
|
||||
dumper.depth = 4
|
||||
dumper.include_filter = [
|
||||
@@ -139,14 +135,14 @@ class BlArmature(ReplicatedDatablock):
|
||||
'name',
|
||||
'layers',
|
||||
]
|
||||
data = dumper.dump(datablock)
|
||||
data = dumper.dump(instance)
|
||||
|
||||
for bone in datablock.bones:
|
||||
for bone in instance.bones:
|
||||
if bone.parent:
|
||||
data['bones'][bone.name]['parent'] = bone.parent.name
|
||||
# get the parent Object
|
||||
# TODO: Use id_data instead
|
||||
object_users = utils.get_datablock_users(datablock)[0]
|
||||
object_users = utils.get_datablock_users(instance)[0]
|
||||
data['user'] = object_users.uuid
|
||||
data['user_name'] = object_users.name
|
||||
|
||||
@@ -157,25 +153,7 @@ class BlArmature(ReplicatedDatablock):
|
||||
data['user_scene'] = [
|
||||
item.name for item in container_users if isinstance(item, bpy.types.Scene)]
|
||||
|
||||
for bone in datablock.bones:
|
||||
for bone in instance.bones:
|
||||
data['bones'][bone.name]['roll'] = get_roll(bone)
|
||||
|
||||
data['animation_data'] = dump_animation_data(datablock)
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def resolve(data: dict) -> object:
|
||||
uuid = data.get('uuid')
|
||||
name = data.get('name')
|
||||
datablock = resolve_datablock_from_uuid(uuid, bpy.data.armatures)
|
||||
if datablock is None:
|
||||
datablock = bpy.data.armatures.get(name)
|
||||
|
||||
return datablock
|
||||
|
||||
@staticmethod
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
return resolve_animation_dependencies(datablock)
|
||||
|
||||
_type = bpy.types.Armature
|
||||
_class = BlArmature
|
||||
@@ -20,56 +20,47 @@ import bpy
|
||||
import mathutils
|
||||
|
||||
from .dump_anything import Loader, Dumper
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from .bl_datablock import resolve_datablock_from_uuid
|
||||
from .bl_action import dump_animation_data, load_animation_data, resolve_animation_dependencies
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
|
||||
class BlCamera(ReplicatedDatablock):
|
||||
class BlCamera(BlDatablock):
|
||||
bl_id = "cameras"
|
||||
bl_class = bpy.types.Camera
|
||||
bl_check_common = False
|
||||
bl_icon = 'CAMERA_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
def _construct(self, data):
|
||||
return bpy.data.cameras.new(data["name"])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
def _load_implementation(self, data, target):
|
||||
loader = Loader()
|
||||
loader.load(datablock, data)
|
||||
loader.load(target, data)
|
||||
|
||||
dof_settings = data.get('dof')
|
||||
|
||||
load_animation_data(data.get('animation_data'), datablock)
|
||||
|
||||
|
||||
# DOF settings
|
||||
if dof_settings:
|
||||
loader.load(datablock.dof, dof_settings)
|
||||
loader.load(target.dof, dof_settings)
|
||||
|
||||
background_images = data.get('background_images')
|
||||
|
||||
datablock.background_images.clear()
|
||||
|
||||
target.background_images.clear()
|
||||
|
||||
if background_images:
|
||||
for img_name, img_data in background_images.items():
|
||||
img_id = img_data.get('image')
|
||||
if img_id:
|
||||
target_img = datablock.background_images.new()
|
||||
target_img = target.background_images.new()
|
||||
target_img.image = bpy.data.images[img_id]
|
||||
loader.load(target_img, img_data)
|
||||
|
||||
img_user = img_data.get('image_user')
|
||||
if img_user:
|
||||
loader.load(target_img.image_user, img_user)
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
# TODO: background image support
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 3
|
||||
dumper.include_filter = [
|
||||
@@ -110,37 +101,14 @@ class BlCamera(ReplicatedDatablock):
|
||||
'scale',
|
||||
'use_flip_x',
|
||||
'use_flip_y',
|
||||
'image_user',
|
||||
'image',
|
||||
'frame_duration',
|
||||
'frame_start',
|
||||
'frame_offset',
|
||||
'use_cyclic',
|
||||
'use_auto_refresh'
|
||||
'image'
|
||||
]
|
||||
data = dumper.dump(datablock)
|
||||
data['animation_data'] = dump_animation_data(datablock)
|
||||
|
||||
for index, image in enumerate(datablock.background_images):
|
||||
if image.image_user:
|
||||
data['background_images'][index]['image_user'] = dumper.dump(image.image_user)
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def resolve(data: dict) -> object:
|
||||
uuid = data.get('uuid')
|
||||
return resolve_datablock_from_uuid(uuid, bpy.data.cameras)
|
||||
|
||||
@staticmethod
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
return dumper.dump(instance)
|
||||
|
||||
def _resolve_deps_implementation(self):
|
||||
deps = []
|
||||
for background in datablock.background_images:
|
||||
for background in self.instance.background_images:
|
||||
if background.image:
|
||||
deps.append(background.image)
|
||||
|
||||
deps.extend(resolve_animation_dependencies(datablock))
|
||||
|
||||
|
||||
return deps
|
||||
|
||||
_type = bpy.types.Camera
|
||||
_class = BlCamera
|
||||
|
||||
@@ -19,12 +19,10 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
|
||||
from deepdiff import DeepDiff, Delta
|
||||
|
||||
from .. import utils
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from .bl_datablock import BlDatablock
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import resolve_datablock_from_uuid
|
||||
|
||||
|
||||
def dump_collection_children(collection):
|
||||
collection_children = []
|
||||
@@ -83,82 +81,58 @@ def resolve_collection_dependencies(collection):
|
||||
|
||||
return deps
|
||||
|
||||
class BlCollection(ReplicatedDatablock):
|
||||
class BlCollection(BlDatablock):
|
||||
bl_id = "collections"
|
||||
bl_icon = 'FILE_FOLDER'
|
||||
bl_class = bpy.types.Collection
|
||||
bl_check_common = True
|
||||
bl_reload_parent = False
|
||||
|
||||
def _construct(self, data):
|
||||
if self.is_library:
|
||||
with bpy.data.libraries.load(filepath=bpy.data.libraries[self.data['library']].filepath, link=True) as (sourceData, targetData):
|
||||
targetData.collections = [
|
||||
name for name in sourceData.collections if name == self.data['name']]
|
||||
|
||||
use_delta = True
|
||||
instance = bpy.data.collections[self.data['name']]
|
||||
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
instance = bpy.data.collections.new(data["name"])
|
||||
return instance
|
||||
|
||||
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
def _load_implementation(self, data, target):
|
||||
loader = Loader()
|
||||
loader.load(datablock, data)
|
||||
loader.load(target, data)
|
||||
|
||||
# Objects
|
||||
load_collection_objects(data['objects'], datablock)
|
||||
load_collection_objects(data['objects'], target)
|
||||
|
||||
# Link childrens
|
||||
load_collection_childrens(data['children'], datablock)
|
||||
load_collection_childrens(data['children'], target)
|
||||
|
||||
# FIXME: Find a better way after the replication big refacotoring
|
||||
# Keep other user from deleting collection object by flushing their history
|
||||
utils.flush_history()
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
dumper.include_filter = [
|
||||
"name",
|
||||
"instance_offset"
|
||||
]
|
||||
data = dumper.dump(datablock)
|
||||
data = dumper.dump(instance)
|
||||
|
||||
# dump objects
|
||||
data['objects'] = dump_collection_objects(datablock)
|
||||
data['objects'] = dump_collection_objects(instance)
|
||||
|
||||
# dump children collections
|
||||
data['children'] = dump_collection_children(datablock)
|
||||
data['children'] = dump_collection_children(instance)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@staticmethod
|
||||
def resolve(data: dict) -> object:
|
||||
uuid = data.get('uuid')
|
||||
return resolve_datablock_from_uuid(uuid, bpy.data.collections)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
return resolve_collection_dependencies(datablock)
|
||||
|
||||
@staticmethod
|
||||
def compute_delta(last_data: dict, current_data: dict) -> Delta:
|
||||
diff_params = {
|
||||
'ignore_order': True,
|
||||
'report_repetition': True
|
||||
}
|
||||
delta_params = {
|
||||
# 'mutate': True
|
||||
}
|
||||
|
||||
return Delta(
|
||||
DeepDiff(last_data,
|
||||
current_data,
|
||||
cache_size=5000,
|
||||
**diff_params),
|
||||
**delta_params)
|
||||
|
||||
_type = bpy.types.Collection
|
||||
_class = BlCollection
|
||||
def _resolve_deps_implementation(self):
|
||||
return resolve_collection_dependencies(self.instance)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user