878 lines
26 KiB
Python
878 lines
26 KiB
Python
from typing import Set
|
|
|
|
# import shutil
|
|
from pathlib import Path
|
|
import subprocess
|
|
import importlib
|
|
import time
|
|
import json
|
|
|
|
import bpy
|
|
from bpy_extras import asset_utils
|
|
from bpy.types import Context, Operator
|
|
from bpy.props import BoolProperty, EnumProperty, StringProperty, IntProperty
|
|
|
|
# from asset_library.constants import (DATA_TYPES, DATA_TYPE_ITEMS, MODULE_DIR)
|
|
import asset_library
|
|
from asset_library.common.bl_utils import (
|
|
attr_set,
|
|
get_addon_prefs,
|
|
get_bl_cmd,
|
|
get_view3d_persp,
|
|
# suitable_areas,
|
|
refresh_asset_browsers,
|
|
load_datablocks,
|
|
)
|
|
|
|
from asset_library.common.file_utils import open_blender_file, synchronize
|
|
from asset_library.common.functions import get_active_library, asset_warning_callback
|
|
|
|
from textwrap import dedent
|
|
from tempfile import gettempdir
|
|
import gpu
|
|
from gpu_extras.batch import batch_for_shader
|
|
import blf
|
|
import bgl
|
|
|
|
|
|
class ASSETLIB_OT_remove_assets(Operator):
|
|
bl_idname = "assetlib.remove_assets"
|
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
bl_label = "Remove Assets"
|
|
bl_description = "Remove Selected Assets"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
|
|
return False
|
|
|
|
sp = context.space_data
|
|
if sp.params.asset_library_ref == "LOCAL":
|
|
return False
|
|
|
|
return True
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
asset = context.active_file
|
|
|
|
lib = get_active_library()
|
|
lib_type = lib.library_type
|
|
|
|
catalog = lib.read_catalog()
|
|
|
|
if not catalog.context.item:
|
|
self.report({"ERROR"}, "The active asset is not in the catalog")
|
|
return {"CANCELLED"}
|
|
|
|
asset_name = context.asset_file_handle.name
|
|
asset_path = lib_type.format_path(asset.asset_data["filepath"])
|
|
asset_catalog = catalog.context.path
|
|
|
|
img_path = lib_type.get_image_path(
|
|
name=asset_name, catalog=asset_catalog, filepath=asset_path
|
|
)
|
|
video_path = lib_type.get_video_path(
|
|
name=asset_name, catalog=asset_catalog, filepath=asset_path
|
|
)
|
|
|
|
if asset_path and asset_path.exists():
|
|
asset_path.unlink()
|
|
if img_path and img_path.exists():
|
|
img_path.unlink()
|
|
if video_path and video_path.exists():
|
|
video_path.unlink()
|
|
# open_blender_file(filepath)
|
|
|
|
try:
|
|
asset_path.parent.rmdir()
|
|
except Exception: # Directory not empty
|
|
pass
|
|
|
|
bpy.ops.assetlib.bundle(name=lib.name, blocking=True)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_edit_data(Operator):
|
|
bl_idname = "assetlib.edit_data"
|
|
bl_label = "Edit Asset Data"
|
|
bl_description = "Edit Current Asset Data"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
warning: StringProperty(name="")
|
|
path: StringProperty(name="Path")
|
|
catalog: StringProperty(
|
|
name="Catalog", update=asset_warning_callback, options={"TEXTEDIT_UPDATE"}
|
|
)
|
|
name: StringProperty(
|
|
name="Name", update=asset_warning_callback, options={"TEXTEDIT_UPDATE"}
|
|
)
|
|
tags: StringProperty(
|
|
name="Tags", description="Tags need to separate with a comma (,)"
|
|
)
|
|
description: StringProperty(name="Description")
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
|
|
return False
|
|
return True
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
lib = get_active_library()
|
|
if lib.merge_libraries:
|
|
lib = prefs.libraries[lib.store_library]
|
|
|
|
new_name = lib.library_type.norm_file_name(self.name)
|
|
new_asset_path = lib.library_type.get_asset_path(
|
|
name=new_name, catalog=self.catalog
|
|
)
|
|
|
|
# asset_data = lib.library_type.get_asset_data(self.asset)
|
|
asset_data = dict(
|
|
tags=[t.strip() for t in self.tags.split(",") if t],
|
|
description=self.description,
|
|
)
|
|
|
|
# lib.library_type.set_asset_catalog(asset, asset_data, catalog_data)
|
|
self.asset.name = self.name
|
|
lib.library_type.set_asset_tags(self.asset, asset_data)
|
|
lib.library_type.set_asset_info(self.asset, asset_data)
|
|
|
|
self.old_asset_path.unlink()
|
|
lib.library_type.write_asset(asset=self.asset, asset_path=new_asset_path)
|
|
|
|
if self.old_image_path.exists():
|
|
new_img_path = lib.library_type.get_image_path(
|
|
new_name, self.catalog, new_asset_path
|
|
)
|
|
self.old_image_path.rename(new_img_path)
|
|
|
|
if self.old_video_path.exists():
|
|
new_video_path = lib.library_type.get_video_path(
|
|
new_name, self.catalog, new_asset_path
|
|
)
|
|
self.old_video_path.rename(new_video_path)
|
|
|
|
# if self.old_description_path.exists():
|
|
# self.old_description_path.unlink()
|
|
|
|
try:
|
|
self.old_asset_path.parent.rmdir()
|
|
except Exception: # The folder is not empty
|
|
pass
|
|
|
|
diff_path = Path(bpy.app.tempdir, "diff.json")
|
|
diff = [
|
|
dict(
|
|
name=self.old_asset_name,
|
|
catalog=self.old_catalog,
|
|
filepath=str(self.old_asset_path),
|
|
operation="REMOVE",
|
|
)
|
|
]
|
|
|
|
asset_data = lib.library_type.get_asset_data(self.asset)
|
|
diff += [
|
|
dict(
|
|
asset_data,
|
|
image=str(new_img_path),
|
|
filepath=str(new_asset_path),
|
|
type=lib.data_type,
|
|
library_id=lib.id,
|
|
catalog=self.catalog,
|
|
operation="ADD",
|
|
)
|
|
]
|
|
|
|
print(diff)
|
|
|
|
diff_path.write_text(json.dumps(diff, indent=4), encoding="utf-8")
|
|
|
|
bpy.ops.assetlib.bundle(name=lib.name, diff=str(diff_path), blocking=True)
|
|
|
|
return {"FINISHED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.separator()
|
|
|
|
layout.use_property_split = True
|
|
|
|
lib = get_active_library()
|
|
|
|
if lib.merge_libraries:
|
|
layout.prop(lib, "store_library", expand=False)
|
|
|
|
layout.prop(self, "catalog", text="Catalog")
|
|
layout.prop(self, "name", text="Name")
|
|
layout.prop(self, "tags")
|
|
layout.prop(self, "description")
|
|
|
|
# layout.prop()
|
|
|
|
layout.separator()
|
|
col = layout.column()
|
|
col.use_property_split = False
|
|
# row.enabled = False
|
|
|
|
if self.path:
|
|
col.label(text=self.path)
|
|
|
|
if self.warning:
|
|
col.label(icon="ERROR", text=self.warning)
|
|
|
|
def invoke(self, context, event):
|
|
|
|
lib = get_active_library()
|
|
|
|
active_lib = lib.library_type.get_active_asset_library()
|
|
|
|
lib.store_library = active_lib.name
|
|
|
|
asset_handle = context.asset_file_handle
|
|
|
|
catalog_file = lib.library_type.read_catalog()
|
|
catalog_ids = {
|
|
v["id"]: {"path": k, "name": v["name"]} for k, v in catalog_file.items()
|
|
}
|
|
|
|
# asset_handle = context.asset_file_handle
|
|
self.old_asset_name = asset_handle.name
|
|
self.old_asset_path = lib.library_type.get_active_asset_path()
|
|
|
|
self.asset = load_datablocks(
|
|
self.old_asset_path, self.old_asset_name, type=lib.data_types
|
|
)
|
|
|
|
if not self.asset:
|
|
self.report({"ERROR"}, "No asset found")
|
|
|
|
self.name = self.old_asset_name
|
|
self.description = asset_handle.asset_data.description
|
|
|
|
tags = [t.strip() for t in self.asset.asset_data.tags.keys() if t]
|
|
self.tags = ", ".join(tags)
|
|
# asset_path
|
|
self.old_catalog = catalog_ids[asset_handle.asset_data.catalog_id]["path"]
|
|
self.catalog = self.old_catalog
|
|
|
|
self.old_image_path = lib.library_type.get_image_path(
|
|
name=self.name, catalog=self.catalog, filepath=self.old_asset_path
|
|
)
|
|
self.old_video_path = lib.library_type.get_video_path(
|
|
name=self.name, catalog=self.catalog, filepath=self.old_asset_path
|
|
)
|
|
|
|
# self.old_description_path = lib.library_type.get_description_path(self.old_asset_path)
|
|
|
|
# self.old_asset_info = lib.library_type.read_asset_info_file(self.old_asset_path)
|
|
# self.old_asset_info = lib.library_type.norm_asset_datas([self.old_asset_info])[0]
|
|
|
|
return context.window_manager.invoke_props_dialog(self, width=450)
|
|
|
|
def cancel(self, context):
|
|
print("Cancel Edit Data, removing the asset")
|
|
|
|
lib = get_active_library()
|
|
active_lib = lib.library_type.get_active_asset_library()
|
|
|
|
getattr(bpy.data, active_lib.data_types).remove(self.asset)
|
|
|
|
|
|
class ASSETLIB_OT_remove_user_library(Operator):
|
|
bl_idname = "assetlib.remove_user_library"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Remove User Library"
|
|
bl_description = "Remove User Library"
|
|
|
|
index: IntProperty(default=-1)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
prefs.user_libraries.remove(self.index)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_add_user_library(Operator):
|
|
bl_idname = "assetlib.add_user_library"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Add User Library"
|
|
bl_description = "Add User Library"
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
lib = prefs.user_libraries.add()
|
|
lib.expand = True
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_open_blend(Operator):
|
|
bl_idname = "assetlib.open_blend"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Open Blender File"
|
|
bl_description = "Open blender file"
|
|
|
|
# filepath : StringProperty(subtype='FILE_PATH')
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
# asset = context.active_file
|
|
# prefs = get_addon_prefs()
|
|
|
|
lib = get_active_library()
|
|
|
|
# filepath = lib.library_type.format_path(asset.asset_data['filepath'])
|
|
|
|
filepath = lib.library_type.get_active_asset_path()
|
|
|
|
open_blender_file(filepath)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_set_paths(Operator):
|
|
bl_idname = "assetlib.set_paths"
|
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
bl_label = "Set Paths"
|
|
bl_description = "Set Library Paths"
|
|
|
|
name: StringProperty()
|
|
all: BoolProperty(default=False)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
print("Set Paths")
|
|
if self.all:
|
|
libs = prefs.libraries
|
|
else:
|
|
libs = [prefs.libraries[self.name]]
|
|
|
|
for lib in libs:
|
|
lib.clear_library_path()
|
|
lib.set_library_path()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_bundle_library(Operator):
|
|
bl_idname = "assetlib.bundle"
|
|
bl_options = {"INTERNAL"}
|
|
bl_label = "Bundle Library"
|
|
bl_description = "Bundle all matching asset found inside one blend"
|
|
|
|
name: StringProperty()
|
|
diff: StringProperty()
|
|
blocking: BoolProperty(default=False)
|
|
mode: EnumProperty(
|
|
items=[
|
|
(i.replace(" ", "_").upper(), i, "") for i in ("None", "All", "Auto Bundle")
|
|
],
|
|
default="NONE",
|
|
)
|
|
directory: StringProperty(subtype="DIR_PATH")
|
|
# conform : BoolProperty(default=False)
|
|
# def refresh(self):
|
|
# for area in suitable_areas(bpy.context.screen):
|
|
# bpy.ops.asset.library_refresh({"area": area, 'region': area.regions[3]})
|
|
# space_data.activate_asset_by_id(asset, deferred=deferred)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
libs = []
|
|
if self.name:
|
|
libs += [prefs.libraries[self.name]]
|
|
|
|
if self.mode == "ALL":
|
|
libs += prefs.libraries.values()
|
|
elif self.mode == "AUTO_BUNDLE":
|
|
libs += [l for l in prefs.libraries if l.auto_bundle]
|
|
|
|
if not libs:
|
|
return {"CANCELLED"}
|
|
|
|
lib_datas = [l.to_dict() for l in libs]
|
|
|
|
print(f"Bundle Libraries: {[l.name for l in libs]}")
|
|
|
|
script_code = dedent(
|
|
f"""
|
|
import bpy
|
|
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
|
|
|
for lib_data in {lib_datas}:
|
|
lib = prefs.env_libraries.add()
|
|
lib.set_dict(lib_data)
|
|
lib.library_type.bundle(cache_diff='{self.diff}')
|
|
|
|
bpy.ops.wm.quit_blender()
|
|
"""
|
|
)
|
|
|
|
script_path = Path(bpy.app.tempdir) / "bundle_library.py"
|
|
script_path.write_text(script_code)
|
|
|
|
print(script_code)
|
|
|
|
# raise Exception()
|
|
|
|
cmd = get_bl_cmd(script=str(script_path), background=True)
|
|
|
|
# print(cmd)
|
|
if self.blocking:
|
|
subprocess.call(cmd)
|
|
bpy.app.timers.register(refresh_asset_browsers, first_interval=0.2)
|
|
else:
|
|
subprocess.Popen(cmd)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_reload_addon(Operator):
|
|
bl_idname = "assetlib.reload_addon"
|
|
bl_options = {"UNDO"}
|
|
bl_label = "Reload Asset Library Addon"
|
|
bl_description = "Reload The Asset Library Addon and the addapters"
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
|
|
print("Execute reload")
|
|
|
|
asset_library.unregister()
|
|
importlib.reload(asset_library)
|
|
asset_library.register()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_diff(Operator):
|
|
bl_idname = "assetlib.diff"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Synchronize"
|
|
bl_description = "Synchronize Action Lib to Local Directory"
|
|
|
|
name: StringProperty()
|
|
conform: BoolProperty(default=False)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
lib = prefs.libraries.get(self.name)
|
|
lib.library_type.diff()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
'''
|
|
class ASSETLIB_OT_conform_library(Operator):
|
|
bl_idname = "assetlib.conform_library"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Conform Library"
|
|
bl_description = "Split each assets per blend and externalize preview"
|
|
|
|
name : StringProperty()
|
|
template_image : StringProperty()
|
|
template_video : StringProperty()
|
|
directory : StringProperty(subtype='DIR_PATH', name='Filepath')
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
|
|
lib = prefs.libraries.get(self.name)
|
|
#lib.library_type.conform(self.directory)
|
|
|
|
templates = {}
|
|
if self.template_image:
|
|
templates['image'] = self.template_image
|
|
if self.template_video:
|
|
templates['video'] = self.template_video
|
|
|
|
|
|
script_path = Path(bpy.app.tempdir) / 'bundle_library.py'
|
|
script_code = dedent(f"""
|
|
import bpy
|
|
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
|
lib = prefs.env_libraries.add()
|
|
lib.set_dict({lib.to_dict()})
|
|
lib.library_type.conform(directory='{self.directory}', templates={templates})
|
|
""")
|
|
|
|
script_path.write_text(script_code)
|
|
|
|
cmd = get_bl_cmd(script=str(script_path), background=True)
|
|
|
|
subprocess.Popen(cmd)
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {'RUNNING_MODAL'}
|
|
'''
|
|
|
|
|
|
class ASSETLIB_OT_make_custom_preview(Operator):
|
|
bl_idname = "assetlib.make_custom_preview"
|
|
bl_label = "Custom Preview"
|
|
bl_description = "Set a camera to preview an asset"
|
|
|
|
image_size: IntProperty(default=512)
|
|
modal: BoolProperty(default=False)
|
|
|
|
def modal(self, context, event):
|
|
if event.type in {"ESC"}: # Cancel
|
|
self.restore()
|
|
return {"CANCELLED"}
|
|
|
|
elif event.type in {"RET", "NUMPAD_ENTER"}: # Cancel
|
|
return self.execute(context)
|
|
# return {'FINISHED'}
|
|
|
|
return {"PASS_THROUGH"}
|
|
|
|
def execute(self, context):
|
|
|
|
prefs = get_addon_prefs()
|
|
bpy.ops.render.opengl(write_still=True)
|
|
|
|
img_path = context.scene.render.filepath
|
|
|
|
# print('Load Image to previews')
|
|
prefs.previews.load(Path(img_path).stem, img_path, "IMAGE")
|
|
# img = bpy.data.images.load(context.scene.render.filepath)
|
|
# img.update()
|
|
# img.preview_ensure()
|
|
|
|
# Copy the image with a new name
|
|
# render = bpy.data.images['Render Result']
|
|
|
|
# render_pixels = [0] * self.image_size * self.image_size * 4
|
|
# render.pixels.foreach_get(render_pixels)
|
|
# img = bpy.data.images.new(name=img_name, width=self.image_size, height=self.image_size, is_data=True, alpha=True)
|
|
# img.pixels.foreach_set(render_pixels)
|
|
|
|
# img.scale(128, 128)
|
|
# img.preview_ensure()
|
|
|
|
# preview_size = render.size
|
|
|
|
# pixels = [0] * preview_size[0] * preview_size[1] * 4
|
|
# render.pixels.foreach_get(pixels)
|
|
|
|
# image.preview.image_size = preview_size
|
|
# image.preview.image_pixels_float.foreach_set(pixels)
|
|
|
|
self.restore()
|
|
|
|
# self.is_running = False
|
|
prefs.preview_modal = False
|
|
|
|
return {"FINISHED"}
|
|
|
|
def restore(self):
|
|
print("RESTORE")
|
|
try:
|
|
bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW")
|
|
except:
|
|
print("Failed remove handler")
|
|
pass
|
|
|
|
bpy.data.objects.remove(self.camera)
|
|
self.attr_changed.restore()
|
|
|
|
def draw_callback_px(self, context):
|
|
if context.space_data != self._space_data:
|
|
return
|
|
|
|
dpi = context.preferences.system.dpi
|
|
|
|
bg_color = (0.8, 0.1, 0.1, 0.5)
|
|
font_color = (1, 1, 1, 1)
|
|
text = f"Escape: Cancel Enter: Make Preview"
|
|
font_id = 0
|
|
dim = blf.dimensions(font_id, text)
|
|
|
|
# gpu.state.line_width_set(100)
|
|
# bgl.glLineWidth(100)
|
|
# self.shader_2d.bind()
|
|
# self.shader_2d.uniform_float("color", bg_color)
|
|
# self.screen_framing.draw(self.shader_2d)
|
|
|
|
# # Reset
|
|
# gpu.state.line_width_set(1)
|
|
|
|
# -dim[0]/2, +dim[1]/2 + 5
|
|
|
|
# Display Text
|
|
blf.color(font_id, *font_color) # unpack color
|
|
blf.position(font_id, context.region.width / 2 - dim[0] / 2, dim[1] / 2 + 5, 0)
|
|
blf.size(font_id, 12, dpi)
|
|
blf.draw(font_id, f"Escape: Cancel Enter: Make Preview")
|
|
|
|
def get_image_name(self):
|
|
prefs = get_addon_prefs()
|
|
preview_names = [p for p in prefs.previews.keys()]
|
|
preview_names.sort()
|
|
|
|
index = 0
|
|
if preview_names:
|
|
index = int(preview_names[-1][-2:]) + 1
|
|
|
|
return f"preview_{index:03d}"
|
|
|
|
def invoke(self, context, event):
|
|
prefs = get_addon_prefs()
|
|
cam_data = bpy.data.cameras.new(name="Preview Camera")
|
|
self.camera = bpy.data.objects.new(name="Preview Camera", object_data=cam_data)
|
|
|
|
# view_3d = get_view3d_persp()
|
|
|
|
scn = context.scene
|
|
space = context.space_data
|
|
|
|
matrix = space.region_3d.view_matrix.inverted()
|
|
if space.region_3d.view_perspective == "CAMERA":
|
|
matrix = scn.camera.matrix_world
|
|
|
|
self.camera.matrix_world = matrix
|
|
|
|
img_name = self.get_image_name()
|
|
img_path = Path(bpy.app.tempdir, img_name).with_suffix(".webp")
|
|
|
|
self.attr_changed = attr_set(
|
|
[
|
|
(space.overlay, "show_overlays", False),
|
|
(space.region_3d, "view_perspective", "CAMERA"),
|
|
(space.region_3d, "view_camera_offset"),
|
|
(space.region_3d, "view_camera_zoom"),
|
|
(space, "lock_camera", True),
|
|
(space, "show_region_ui", False),
|
|
(scn, "camera", self.camera),
|
|
(scn.render, "resolution_percentage", 100),
|
|
(scn.render, "resolution_x", self.image_size),
|
|
(scn.render, "resolution_y", self.image_size),
|
|
(scn.render, "film_transparent", True),
|
|
(scn.render.image_settings, "file_format", "WEBP"),
|
|
(scn.render.image_settings, "color_mode", "RGBA"),
|
|
# (scn.render.image_settings, 'color_depth', '8'),
|
|
(scn.render, "use_overwrite", True),
|
|
(scn.render, "filepath", str(img_path)),
|
|
]
|
|
)
|
|
|
|
bpy.ops.view3d.view_center_camera()
|
|
space.region_3d.view_camera_zoom -= 6
|
|
space.region_3d.view_camera_offset[1] += 0.03
|
|
|
|
w, h = (context.region.width, context.region.height)
|
|
|
|
self._space_data = context.space_data
|
|
|
|
if self.modal:
|
|
prefs.preview_modal = True
|
|
|
|
self.shader_2d = gpu.shader.from_builtin("2D_UNIFORM_COLOR")
|
|
self.screen_framing = batch_for_shader(
|
|
self.shader_2d, "LINE_LOOP", {"pos": [(0, 0), (0, h), (w, h), (w, 0)]}
|
|
)
|
|
|
|
self._handle = bpy.types.SpaceView3D.draw_handler_add(
|
|
self.draw_callback_px, (context,), "WINDOW", "POST_PIXEL"
|
|
)
|
|
context.window_manager.modal_handler_add(self)
|
|
|
|
return {"RUNNING_MODAL"}
|
|
else:
|
|
return self.execute(context)
|
|
|
|
|
|
class ASSETLIB_OT_generate_previews(Operator):
|
|
bl_idname = "assetlib.generate_previews"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Generate Previews"
|
|
bl_description = "Generate and write the image for assets"
|
|
|
|
cache: StringProperty()
|
|
preview_blend: StringProperty()
|
|
name: StringProperty()
|
|
blocking: BoolProperty(default=True)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
prefs = get_addon_prefs()
|
|
lib = prefs.libraries.get(self.name)
|
|
# self.write_file(self.diff_file, self.diff)
|
|
|
|
# preview_assets = [(a.asset_data['filepath'], self.data_types, a.name) for a in assets]
|
|
|
|
# self.preview_assets_file.write_text(json.dumps(preview_assets), encoding='utf-8')
|
|
|
|
# cmd = [
|
|
# bpy.app.binary_path, '-b', '--use-system-env',
|
|
# '--python', str(PREVIEW_ASSETS_SCRIPT), '--',
|
|
# '--preview-blend', str(self.preview_blend),
|
|
# '--preview-assets-file', str(self.preview_assets_file)
|
|
# ]
|
|
# subprocess.call(cmd)
|
|
preview_blend = self.preview_blend or lib.library_type.preview_blend
|
|
|
|
if not preview_blend or not Path(preview_blend).exists():
|
|
preview_blend = MODULE_DIR / "common" / "preview.blend"
|
|
|
|
script_path = Path(bpy.app.tempdir) / "generate_previews.py"
|
|
script_code = dedent(
|
|
f"""
|
|
import bpy
|
|
prefs = bpy.context.preferences.addons["asset_library"].preferences
|
|
lib = prefs.env_libraries.add()
|
|
lib.set_dict({lib.to_dict()})
|
|
|
|
bpy.ops.wm.open_mainfile(filepath='{preview_blend}', load_ui=True)
|
|
lib.library_type.generate_previews(cache='{self.cache}')
|
|
"""
|
|
)
|
|
|
|
script_path.write_text(script_code)
|
|
|
|
cmd = get_bl_cmd(script=str(script_path), background=True)
|
|
|
|
if self.blocking:
|
|
subprocess.call(cmd)
|
|
else:
|
|
subprocess.Popen(cmd)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_play_preview(Operator):
|
|
bl_idname = "assetlib.play_preview"
|
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
bl_label = "Play Preview"
|
|
bl_description = "Play Preview"
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
if not context.active_file:
|
|
return False
|
|
|
|
if not asset_utils.SpaceAssetInfo.is_asset_browser(context.space_data):
|
|
cls.poll_message_set("Current editor is not an asset browser")
|
|
return False
|
|
|
|
lib = get_active_library()
|
|
if not lib:
|
|
return False
|
|
|
|
return True
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
asset = context.active_file
|
|
prefs = get_addon_prefs()
|
|
|
|
lib = get_active_library()
|
|
|
|
# filepath = lib.library_type.format_path(asset.asset_data['filepath'])
|
|
asset_path = lib.library_type.get_active_asset_path()
|
|
|
|
asset_image = lib.library_type.get_image(asset.name, asset_path)
|
|
asset_video = lib.library_type.get_video(asset.name, asset_path)
|
|
|
|
if not asset_image and not asset_video:
|
|
self.report({"ERROR"}, f"Preview for {asset.name} not found.")
|
|
return {"CANCELLED"}
|
|
|
|
if asset_video:
|
|
self.report({"INFO"}, f"Video found. {asset_video}.")
|
|
|
|
if prefs.video_player:
|
|
subprocess.Popen([prefs.video_player, asset_video])
|
|
else:
|
|
bpy.ops.wm.path_open(filepath=str(asset_video))
|
|
else:
|
|
self.report({"INFO"}, f"Image found. {asset_image}.")
|
|
|
|
if prefs.image_player:
|
|
subprocess.Popen([prefs.image_player, asset_image])
|
|
else:
|
|
bpy.ops.wm.path_open(filepath=str(asset_image))
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ASSETLIB_OT_synchronize(Operator):
|
|
bl_idname = "assetlib.synchronize"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
bl_label = "Synchronize"
|
|
bl_description = "Synchronize Action Lib to Local Directory"
|
|
|
|
clean: BoolProperty(default=False)
|
|
only_new: BoolProperty(default=False)
|
|
only_recent: BoolProperty(default=False)
|
|
name: StringProperty()
|
|
all: BoolProperty(default=False)
|
|
|
|
def execute(self, context: Context) -> Set[str]:
|
|
|
|
print("Not yet Implemented, have to be replace by Bundle instead")
|
|
return {"FINISHED"}
|
|
|
|
prefs = get_addon_prefs()
|
|
print("Synchronize")
|
|
if self.all:
|
|
libs = prefs.libraries
|
|
else:
|
|
libs = [prefs.libraries.get(self.name)]
|
|
|
|
for lib in libs:
|
|
if self.clean and Path(lib.path_local).exists():
|
|
pass
|
|
print("To check first")
|
|
# shutil.rmtree(path_local)
|
|
|
|
if not lib.path_local:
|
|
continue
|
|
|
|
synchronize(
|
|
src=lib.path,
|
|
dst=lib.path_local,
|
|
only_new=self.only_new,
|
|
only_recent=self.only_recent,
|
|
)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
classes = (
|
|
ASSETLIB_OT_play_preview,
|
|
ASSETLIB_OT_open_blend,
|
|
ASSETLIB_OT_set_paths,
|
|
ASSETLIB_OT_synchronize,
|
|
ASSETLIB_OT_add_user_library,
|
|
ASSETLIB_OT_remove_user_library,
|
|
ASSETLIB_OT_diff,
|
|
ASSETLIB_OT_generate_previews,
|
|
ASSETLIB_OT_bundle_library,
|
|
ASSETLIB_OT_remove_assets,
|
|
ASSETLIB_OT_edit_data,
|
|
# ASSETLIB_OT_conform_library,
|
|
ASSETLIB_OT_reload_addon,
|
|
ASSETLIB_OT_make_custom_preview,
|
|
)
|
|
|
|
|
|
def register():
|
|
# bpy.types.UserAssetLibrary.is_env = False
|
|
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
|
|
|
|
def unregister():
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls)
|