Compare commits

..

56 Commits

Author SHA1 Message Date
Fabian de9255f71c feat: presence overlay button+UInfo in side panel 2021-08-19 18:04:07 +02:00
Swann Martinez 99528ea3e0 Merge branch '232-fix-ui-host-and-lobby' into 'develop'
Resolve "fix ui host and lobby"

See merge request slumber/multi-user!152
2021-08-16 14:03:16 +00:00
Fabian bb342951a5 fix: lobby init 2021-08-16 15:59:19 +02:00
Fabian 438a79177b fix: host solo 2021-08-16 12:02:10 +02:00
Swann 08fc49c40f fix: session private by default 2021-07-30 14:09:40 +02:00
Swann d7e25b1192 fix: clean docker file 2021-07-30 13:47:31 +02:00
Swann 1671422143 Merge branch 'develop' of gitlab.com:slumber/multi-user into develop 2021-07-30 13:17:29 +02:00
Swann a9620c0752 fix: docker server command 2021-07-30 13:16:43 +02:00
Swann Martinez 583beaf6fe Merge branch '231-server-public-session-private-issue' into 'develop'
Server "public session" private issue

See merge request slumber/multi-user!151
2021-07-28 15:34:24 +00:00
Fabian 126d2338f2 fix: server psrwd issue 2021-07-28 17:33:07 +02:00
Swann 24b0c0ed8a fix: get active server preset 2021-07-27 17:03:44 +02:00
Swann 07fc1cf000 fix: enable tests back 2021-07-27 11:08:57 +02:00
Swann 8e0131b3a8 feat: temporary disable test before fixing blender addon_tester 2021-07-26 18:29:06 +02:00
Swann 912a2d524c feat: try disable operator tests 2021-07-26 18:19:24 +02:00
Swann 82a5124d64 fix: unit tests 2021-07-26 18:16:21 +02:00
Swann cca5bf903b fix: replication deployment 2021-07-26 18:05:36 +02:00
Swann Martinez 4c0d4cb1c7 Merge branch '218-new-ui-ux-implementation' into 'develop'
New UI/UX implementation

See merge request slumber/multi-user!140
2021-07-26 15:52:19 +00:00
Fabian ca64797641 Merge branch 'develop' into 218-new-ui-ux-implementation 2021-07-26 17:51:01 +02:00
Fabian a49d9ee437 feat: server ping timeout preferences 2021-07-26 17:42:13 +02:00
Fabian 4c1cd6b8f8 fix: review 2021-07-26 17:30:56 +02:00
Fabian d6cda709a6 fix: replication conflict 2021-07-26 15:46:29 +02:00
Swann 4bc0feb3a5 fix: ReferenceError in update_external dependency by removing orphan nodes. 2021-07-23 19:35:56 +02:00
Fabian 59aab7159a fix: remove logging.info 2021-07-23 17:10:10 +02:00
Fabian 0a798bb21b feat: clean files+add repository sync icons 2021-07-23 12:51:16 +02:00
Swann beaafce4fa Merge branch 'develop' into 218-new-ui-ux-implementation 2021-07-22 11:01:59 +02:00
Fabian 6f77337832 feat: request (with bug) 2021-07-22 10:55:18 +02:00
Fabian 07252d62df feat: fonctional UI, no server pswd/ping 2021-07-22 09:38:01 +02:00
Fabian ac615cd134 feat: first+server list UI, ping/pswd unfonctional 2021-07-21 11:12:17 +02:00
Swann a4f9f6e051 fix: replication dependencies conflicts 2021-07-20 16:19:53 +02:00
Swann 10de88cdc9 fix: old replication installation conflicts 2021-07-20 16:06:24 +02:00
Swann e4fa34c984 fix: addon version number 2021-07-20 15:37:11 +02:00
Swann 0dd685d009 doc: add missing presence flags 2021-07-20 15:11:38 +02:00
Swann 3e8c30c0ab fix: supported datablocks in readme 2021-07-20 14:59:30 +02:00
Swann 21cc3cd917 fix: update readme to reflect changes 2021-07-20 14:57:52 +02:00
Swann 81e620ee3d fix: documentations capture for 0.4.0 2021-07-20 14:50:33 +02:00
Swann fb9bd108bd feat: update changelog to reflect v0.4.0 version 2021-07-20 14:19:33 +02:00
Fabian 4846fbb589 fix: server list working (no ping/lock/pop-up) 2021-07-19 16:03:12 +02:00
Swann Martinez cab6625399 Merge branch '219-lock-annotation-doesn-t-sync' into 'develop'
Resolve "Lock annotation doesn't sync"

See merge request slumber/multi-user!143
2021-07-14 10:41:32 +00:00
Swann 1b81251a11 fix: annotation lock 2021-07-14 12:38:30 +02:00
Fabian cf44e547a2 fix: presence_text_distance rename 2021-07-13 17:15:34 +02:00
Fabian 0269363c63 fix: overlay UI 2021-07-13 17:14:32 +02:00
Fabian 4ffca17c54 fix: edit>prefs UI/UX 2021-07-13 16:40:26 +02:00
Swann Martinez 77bf269fb5 Merge branch '221-optimize-user-selection-draw-code' into 'develop'
Resolve "Optimize user selection draw code"

See merge request slumber/multi-user!142
2021-07-13 14:37:09 +00:00
Swann 1e675132d4 fix: collection instances index offset 2021-07-13 16:33:46 +02:00
Swann 781287c390 refactor: use one drawcall for all selection bbox 2021-07-13 15:45:08 +02:00
Fabian fc91b252f4 feat: edit>preferences + serverpassword ui 2021-07-12 18:01:35 +02:00
Fabian 41c7c569ca fix: link session status icons to session header 2021-07-12 14:55:41 +02:00
Fabian a82d263f05 feat: add "icons" folder + init 2021-07-12 14:53:18 +02:00
Swann Martinez d4476baa1b Merge branch '220-batch-right-selection-update' into 'develop'
Resolve "Batch right selection update"

See merge request slumber/multi-user!141
2021-07-12 10:20:23 +00:00
Swann 467e98906e feat: Batch right selection update
Related to https://gitlab.com/slumber/multi-user/-/issues/220
2021-07-12 12:06:45 +02:00
Swann 64a25f94a3 fix: gpencil material loading error
Now loading gpencil materials from uuid
2021-07-09 16:59:59 +02:00
Swann Martinez e6996316be Merge branch '215-annotations-doesn-t-sync-correctly' into 'develop'
Resolve "Annotations doesn't sync correctly"

See merge request slumber/multi-user!138
2021-07-07 08:18:49 +00:00
Swann cf4cd94096 refactor: remove gpencil dump stroke legacy
Related to #166 and #215
2021-07-07 10:15:23 +02:00
Swann e9ab633aac fix: annotations updates
Related to #215
2021-07-06 16:06:14 +02:00
Swann 297639e80f fix: crash on changing workspace change 2021-07-06 15:39:19 +02:00
Swann Martinez f0cc63b6f0 Merge branch '214-animated-object-transform-not-correctly-sync' into 'develop'
Resolve "Animated object transform not correctly sync"

See merge request slumber/multi-user!137
2021-07-06 12:32:39 +00:00
31 changed files with 1090 additions and 676 deletions
+31 -1
View File
@@ -186,4 +186,34 @@ All notable changes to this project will be documented in this file.
- Exception access violation during Undo/Redo
- Sync missing armature bone Roll
- Sync missing driver data_path
- Constraint replication
- Constraint replication
## [0.4.0] - 2021-07-20
### Added
- Connection preset system (@Kysios)
- Display connected users active mode (users pannel and viewport) (@Kysios)
- Delta-based replication
- Sync timeline marker
- Sync images settings (@Kysios)
- Sync parent relation type (@Kysios)
- Sync uv project modifier
- Sync FCurves modifiers
### Changed
- User selection optimizations (draw and sync) (@Kysios)
- Improved shapekey syncing performances
- Improved gpencil syncing performances
- Integrate replication as a submodule
- The dependencies are now installed in a folder(blender addon folder) that no longer requires administrative rights
- Presence overlay UI optimization (@Kysios)
### Fixed
- User selection bounding box glitches for non-mesh objects (@Kysios)
- Transforms replication for animated objects
- GPencil fill stroke
- Sculpt and GPencil brushes deleted when joining a session (@Kysios)
- Auto-updater doesn't work for master and develop builds
+31 -32
View File
@@ -11,9 +11,8 @@ This tool aims to allow multiple users to work on the same scene over the networ
## Quick installation
1. Download latest release [multi_user.zip](https://gitlab.com/slumber/multi-user/-/jobs/artifacts/master/download?job=build).
2. Run blender as administrator (dependencies installation).
3. Install last_version.zip from your addon preferences.
1. Download [latest build](https://gitlab.com/slumber/multi-user/-/jobs/artifacts/develop/download?job=build) or [stable build](https://gitlab.com/slumber/multi-user/-/jobs/artifacts/master/download?job=build).
2. Install last_version.zip from your addon preferences.
[Dependencies](#dependencies) will be automatically added to your blender python during installation.
@@ -29,35 +28,35 @@ See the [troubleshooting guide](https://slumber.gitlab.io/multi-user/getting_sta
Currently, not all data-block are supported for replication over the wire. The following list summarizes the status for each ones.
| Name | Status | Comment |
| -------------- | :----: | :----------------------------------------------------------: |
| action | ✔️ | |
| camera | ✔️ | |
| collection | ✔️ | |
| gpencil | ✔️ | |
| image | ✔️ | |
| mesh | ✔️ | |
| material | ✔️ | |
| node_groups | ✔️ | Material & Geometry only |
| geometry nodes | ✔️ | |
| metaball | ✔️ | |
| object | ✔️ | |
| texts | ✔️ | |
| scene | ✔️ | |
| world | ✔️ | |
| volumes | ✔️ | |
| lightprobes | ✔️ | |
| physics | ✔️ | |
| curve | ❗ | Nurbs surfaces not supported |
| textures | ❗ | Supported for modifiers/materials/geo nodes only |
| armature | ❗ | Not stable |
| particles | ❗ | The cache isn't syncing. |
| speakers | ❗ | [Partial](https://gitlab.com/slumber/multi-user/-/issues/65) |
| vse | ❗ | Mask and Clip not supported yet |
| libraries | ❗ | Partial |
| nla | ❌ | |
| texts | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/81) |
| compositing | ❌ | [Planned](https://gitlab.com/slumber/multi-user/-/issues/46) |
| Name | Status | Comment |
| -------------- | :----: | :---------------------------------------------------------------------: |
| action | ✔️ | |
| camera | ✔️ | |
| collection | ✔️ | |
| gpencil | ✔️ | |
| image | ✔️ | |
| mesh | ✔️ | |
| material | ✔️ | |
| node_groups | ✔️ | Material & Geometry only |
| geometry nodes | ✔️ | |
| metaball | ✔️ | |
| object | ✔️ | |
| texts | ✔️ | |
| scene | ✔️ | |
| world | ✔️ | |
| volumes | ✔️ | |
| lightprobes | ✔️ | |
| physics | ✔️ | |
| textures | ✔️ | |
| curve | ❗ | Nurbs surfaces not supported |
| armature | ❗ | Only for Mesh. [Planned for GPencil](https://gitlab.com/slumber/multi-user/-/issues/161). Not stable yet |
| particles | ❗ | The cache isn't syncing. |
| speakers | ❗ | [Partial](https://gitlab.com/slumber/multi-user/-/issues/65) |
| vse | ❗ | Mask and Clip not supported yet |
| libraries | ❌ | |
| nla | ❌ | |
| texts | ❌ | [Planned for v0.5.0](https://gitlab.com/slumber/multi-user/-/issues/81) |
| compositing | ❌ | [Planned for v0.5.0](https://gitlab.com/slumber/multi-user/-/issues/46) |
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 26 KiB

+4 -1
View File
@@ -215,8 +215,10 @@ One of the most vital tools is the **Online user panel**. It lists all connected
users' information including your own:
* **Role** : if a user is an admin or a regular user.
* **Location**: Where the user is actually working.
* **Username** : Name of the user.
* **Mode** : User's active editing mode (edit_mesh, paint,etc.).
* **Frame**: When (on which frame) the user is working.
* **Location**: Where the user is actually working.
* **Ping**: user's connection delay in milliseconds
.. figure:: img/quickstart_users.png
@@ -273,6 +275,7 @@ it draw users' related information in your viewport such as:
* Username
* User point of view
* User active mode
* User selection
.. figure:: img/quickstart_presence.png
+5 -5
View File
@@ -212,14 +212,14 @@ You can run the dedicated server on any platform by following these steps:
.. code-block:: bash
replication.server
replication.serve
.. hint::
You can also specify a custom **port** (-p), **timeout** (-t), **admin password** (-pwd), **log level (ERROR, WARNING, INFO or DEBUG)** (-l) and **log file** (-lf) with the following optional arguments
.. code-block:: bash
replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
Here, for example, a server is instantiated on port 5555, with password 'admin', a 5 second timeout, and logging enabled.
@@ -562,7 +562,7 @@ The default Docker image essentially runs the equivalent of:
.. code-block:: bash
replication.server -pwd admin -p 5555 -t 5000 -l DEBUG -lf multiuser_server.log
replication.serve -pwd admin -p 5555 -t 5000 -l DEBUG -lf multiuser_server.log
This means the server will be launched with 'admin' as the administrator password, run on ports 5555:5558, use a timeout of 5 seconds, verbose 'DEBUG' log level, and with log files written to 'multiuser_server.log'. See :ref:`cmd-line` for a description of optional parameters.
@@ -572,7 +572,7 @@ For example, I would like to launch my server with a different administrator pas
.. code-block:: bash
python3 -m replication.server -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
replication.serve -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
Now, my configuration should look like this:
@@ -691,7 +691,7 @@ We're finally ready to launch the server. Simply run:
.. code-block:: bash
python3 -m replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
See :ref:`cmd-line` for a description of optional parameters
+9 -1
View File
@@ -19,7 +19,7 @@
bl_info = {
"name": "Multi-User",
"author": "Swann Martinez",
"version": (0, 5, 0),
"version": (0, 4, 0),
"description": "Enable real-time collaborative workflow inside blender",
"blender": (2, 82, 0),
"location": "3D View > Sidebar > Multi-User tab",
@@ -61,6 +61,7 @@ def register():
from . import operators
from . import handlers
from . import ui
from . import icons
from . import preferences
from . import addon_updater_ops
@@ -70,6 +71,7 @@ def register():
operators.register()
handlers.register()
ui.register()
icons.register()
except ModuleNotFoundError as e:
raise Exception(module_error_msg)
logging.error(module_error_msg)
@@ -83,7 +85,9 @@ def register():
type=preferences.SessionUser
)
bpy.types.WindowManager.user_index = bpy.props.IntProperty()
bpy.types.WindowManager.server_index = bpy.props.IntProperty()
bpy.types.TOPBAR_MT_file_import.append(operators.menu_func_import)
bpy.types.TOPBAR_MT_file_export.append(operators.menu_func_export)
def unregister():
@@ -91,14 +95,17 @@ def unregister():
from . import operators
from . import handlers
from . import ui
from . import icons
from . import preferences
from . import addon_updater_ops
bpy.types.TOPBAR_MT_file_import.remove(operators.menu_func_import)
bpy.types.TOPBAR_MT_file_export.remove(operators.menu_func_export)
presence.unregister()
addon_updater_ops.unregister()
ui.unregister()
icons.unregister()
handlers.unregister()
operators.unregister()
preferences.unregister()
@@ -107,5 +114,6 @@ def unregister():
del bpy.types.ID.uuid
del bpy.types.WindowManager.online_users
del bpy.types.WindowManager.user_index
del bpy.types.WindowManager.server_index
environment.unregister()
+19 -43
View File
@@ -28,7 +28,8 @@ 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 ..utils import get_preferences
from ..timers import is_annotating
from .bl_material import load_materials_slots, dump_materials_slots
STROKE_POINT = [
'co',
@@ -65,36 +66,9 @@ def dump_stroke(stroke):
:param stroke: target grease pencil stroke
:type stroke: bpy.types.GPencilStroke
:return: dict
:return: (p_count, p_data)
"""
assert(stroke)
dumper = Dumper()
dumper.include_filter = [
"aspect",
"display_mode",
"draw_cyclic",
"end_cap_mode",
"hardeness",
"line_width",
"material_index",
"start_cap_mode",
"uv_rotation",
"uv_scale",
"uv_translation",
"vertex_color_fill",
]
dumped_stroke = dumper.dump(stroke)
# Stoke points
p_count = len(stroke.points)
dumped_stroke['p_count'] = p_count
dumped_stroke['points'] = np_dump_collection(stroke.points, STROKE_POINT)
# TODO: uv_factor, uv_rotation
return dumped_stroke
return (len(stroke.points), np_dump_collection(stroke.points, STROKE_POINT))
def load_stroke(stroke_data, stroke):
@@ -107,12 +81,12 @@ def load_stroke(stroke_data, stroke):
"""
assert(stroke and stroke_data)
stroke.points.add(stroke_data["p_count"])
np_load_collection(stroke_data['points'], stroke.points, STROKE_POINT)
stroke.points.add(stroke_data[0])
np_load_collection(stroke_data[1], stroke.points, STROKE_POINT)
# HACK: Temporary fix to trigger a BKE_gpencil_stroke_geometry_update to
# fix fill issues
stroke.uv_scale = stroke_data["uv_scale"]
stroke.uv_scale = 1.0
def dump_frame(frame):
@@ -147,10 +121,12 @@ def load_frame(frame_data, frame):
assert(frame and frame_data)
# Load stroke points
for stroke_data in frame_data['strokes_points']:
target_stroke = frame.strokes.new()
load_stroke(stroke_data, target_stroke)
# Load stroke metadata
np_load_collection(frame_data['strokes'], frame.strokes, STROKE)
@@ -170,7 +146,6 @@ def dump_layer(layer):
'opacity',
'channel_color',
'color',
# 'thickness', #TODO: enabling only for annotation
'tint_color',
'tint_factor',
'vertex_paint_opacity',
@@ -187,7 +162,7 @@ def dump_layer(layer):
'hide',
'annotation_hide',
'lock',
# 'lock_frame',
'lock_frame',
# 'lock_material',
# 'use_mask_layer',
'use_lights',
@@ -195,12 +170,13 @@ def dump_layer(layer):
'select',
'show_points',
'show_in_front',
# 'thickness'
# 'parent',
# 'parent_type',
# 'parent_bone',
# 'matrix_inverse',
]
if layer.id_data.is_annotation:
if layer.thickness != 0:
dumper.include_filter.append('thickness')
dumped_layer = dumper.dump(layer)
@@ -255,10 +231,10 @@ class BlGpencil(ReplicatedDatablock):
@staticmethod
def load(data: dict, datablock: object):
datablock.materials.clear()
if "materials" in data.keys():
for mat in data['materials']:
datablock.materials.append(bpy.data.materials[mat])
# MATERIAL SLOTS
src_materials = data.get('materials', None)
if src_materials:
load_materials_slots(src_materials, datablock.materials)
loader = Loader()
loader.load(datablock, data)
@@ -286,7 +262,6 @@ class BlGpencil(ReplicatedDatablock):
dumper = Dumper()
dumper.depth = 2
dumper.include_filter = [
'materials',
'name',
'zdepth_offset',
'stroke_thickness_space',
@@ -294,7 +269,7 @@ class BlGpencil(ReplicatedDatablock):
'stroke_depth_order'
]
data = dumper.dump(datablock)
data['materials'] = dump_materials_slots(datablock.materials)
data['layers'] = {}
for layer in datablock.layers:
@@ -323,7 +298,8 @@ class BlGpencil(ReplicatedDatablock):
return bpy.context.mode == 'OBJECT' \
or layer_changed(datablock, data) \
or frame_changed(data) \
or get_preferences().sync_flags.sync_during_editmode
or get_preferences().sync_flags.sync_during_editmode \
or is_annotating(bpy.context)
_type = bpy.types.GreasePencil
_class = BlGpencil
+1 -2
View File
@@ -387,11 +387,10 @@ def load_materials_slots(src_materials: list, dst_materials: bpy.types.bpy_prop_
for mat_uuid, mat_name in src_materials:
mat_ref = None
if mat_uuid is not None:
if mat_uuid:
mat_ref = get_datablock_from_uuid(mat_uuid, None)
else:
mat_ref = bpy.data.materials[mat_name]
dst_materials.append(mat_ref)
+6 -3
View File
@@ -403,8 +403,9 @@ class BlScene(ReplicatedDatablock):
datablock.world = bpy.data.worlds[data['world']]
# Annotation
if 'grease_pencil' in data.keys():
datablock.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
gpencil_uid = data.get('grease_pencil')
if gpencil_uid:
datablock.grease_pencil = resolve_datablock_from_uuid(gpencil_uid, bpy.data.grease_pencils)
if get_preferences().sync_flags.sync_render_settings:
if 'eevee' in data.keys():
@@ -470,7 +471,6 @@ class BlScene(ReplicatedDatablock):
'name',
'world',
'id',
'grease_pencil',
'frame_start',
'frame_end',
'frame_step',
@@ -530,6 +530,9 @@ class BlScene(ReplicatedDatablock):
if datablock.timeline_markers:
data['timeline_markers'] = [(m.name, m.frame, getattr(m.camera, 'uuid', None)) for m in datablock.timeline_markers]
if datablock.grease_pencil:
data['grease_pencil'] = datablock.grease_pencil.uuid
return data
@staticmethod
+5 -2
View File
@@ -79,8 +79,6 @@ def on_scene_update(scene):
logging.debug(f"Ignoring distant update of {dependency_updates[0].id.name}")
return
update_external_dependencies()
# NOTE: maybe we don't need to check each update but only the first
for update in reversed(dependency_updates):
update_uuid = getattr(update.id, 'uuid', None)
@@ -109,6 +107,11 @@ def on_scene_update(scene):
porcelain.commit(session.repository, scn_uuid)
porcelain.push(session.repository, 'origin', scn_uuid)
scene_graph_changed = [u for u in reversed(dependency_updates) if getattr(u.id, 'uuid', None) and isinstance(u.id,(bpy.types.Scene,bpy.types.Collection))]
if scene_graph_changed:
porcelain.purge_orphan_nodes(session.repository)
update_external_dependencies()
@persistent
def resolve_deps_graph(dummy):
+45
View File
@@ -0,0 +1,45 @@
# ##### 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 #####
import bpy
import os
from pathlib import Path
import bpy.utils.previews
def register():
global icons_col
pcoll = bpy.utils.previews.new()
icons_dir = os.path.join(os.path.dirname(__file__), ".")
for png in Path(icons_dir).rglob("*.png"):
pcoll.load(png.stem, str(png), "IMAGE")
icons_col = pcoll
def unregister():
global icons_col
try:
bpy.utils.previews.remove(icons_col)
except Exception:
pass
icons_col = None
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Some files were not shown because too many files have changed in this diff Show More