Skip to content

Commit 6dbfc66

Browse files
committed
Show docs instead of repository for the website
1 parent 0ae4db2 commit 6dbfc66

File tree

6 files changed

+218
-1
lines changed

6 files changed

+218
-1
lines changed

Diff for: blender_manifest.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ maintainer = "Zach Eastin, Richard Traynor"
88
type = "add-on"
99

1010
# Optional link to documentation, support, source files, etc
11-
website = "https://github.com/CGCookie/superhive-blender-addon"
11+
# website = "https://github.com/CGCookie/superhive-blender-addon"
12+
website = "https://github.com/CGCookie/superhive-blender-addon/wiki"
1213

1314
# Optional list defined by Blender and server, see:
1415
# https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html

Diff for: ops/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
export_library,
88
import_from_directory,
99
remove_empty_catalogs,
10+
save_out_preview,
1011
)
1112

1213

Diff for: ops/save_out_preview.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import bpy
2+
from bpy.types import Operator
3+
4+
from . import polls
5+
from .. import utils
6+
7+
8+
class SH_OT_SaveOutPreview(Operator):
9+
bl_idname = "bkeeper.save_out_preview"
10+
bl_label = "Save Preview"
11+
bl_description = "Save the preview of the active asset"
12+
bl_options = {"REGISTER", "UNDO"}
13+
14+
@classmethod
15+
def poll(cls, context):
16+
return polls.is_asset_browser(context, cls=cls) and context.selected_assets
17+
18+
def execute(self, context):
19+
bpy_lib = utils.get_active_bpy_library_from_context(context, area=context.area)
20+
asset = utils.Asset(context.asset)
21+
22+
print("STARTING SAVE OUT PREVIEW")
23+
asset.save_out_preview(bpy_lib.path)
24+
print("FINISHED SAVE OUT PREVIEW")
25+
26+
return {"FINISHED"}
27+
28+
29+
classes = (SH_OT_SaveOutPreview,)
30+
31+
32+
def register():
33+
for cls in classes:
34+
bpy.utils.register_class(cls)
35+
36+
37+
def unregister():
38+
for cls in reversed(classes):
39+
bpy.utils.unregister_class(cls)

Diff for: stand_alone_scripts/save_out_previews.py

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
CLI Script to save out the previews of all assets:
3+
4+
Argument Order:
5+
0. blender executable
6+
1. background arg
7+
2. factory startup arg: str
8+
3. blend file to open: str
9+
4. run python script arg
10+
5. script to run
11+
6. output directory: str
12+
7. asset name: str
13+
8. asset type: str
14+
9. do whole file: bool
15+
"""
16+
17+
import sys
18+
from pathlib import Path
19+
from typing import Any
20+
21+
import bpy
22+
import numpy as np
23+
import OpenImageIO.OpenImageIO as oiio
24+
from OpenImageIO import ROI, ImageBuf, ImageSpec
25+
26+
27+
DIRECTORY = Path(sys.argv[6])
28+
ASSET_NAME = sys.argv[7]
29+
ASSET_TYPE = sys.argv[8]
30+
DO_WHOLE_FILE = sys.argv[9] == "True"
31+
32+
print(f"{DIRECTORY=}")
33+
print(f"{ASSET_NAME=}")
34+
print(f"{ASSET_TYPE=}")
35+
print(f"{DO_WHOLE_FILE=}")
36+
37+
38+
def set_compression(
39+
spec: ImageSpec, extension: str, quality: int = 100, quality_other: Any = None
40+
) -> None:
41+
if extension == ".bmp":
42+
spec.attribute("compression", quality_other) # "rle4" or "rle8"
43+
elif extension in {".jpg", ".jpeg", ".jpe", ".jif", ".jfif", ".jfi"}:
44+
spec.attribute("Compression", f"jpeg:{quality}")
45+
elif extension == ".exr":
46+
# quality_other= "none", "rle", "zips", "zip", "piz", "pxr24", "b44", "b44a", "dwaa", "dwab"
47+
spec.attribute("compression", quality_other)
48+
elif extension == ".png":
49+
# quality needs to be between 0 and 9
50+
quality = quality / 100 * 9
51+
spec.attribute("png:compressionLevel", quality)
52+
elif extension in {".tif", ".tiff", ".tx", ".env", ".sm", ".vsm"}:
53+
# quality_other= "none", "lzw", "zip", "ccittrle", "jpeg", "backbits"
54+
spec.attribute("compression", quality_other)
55+
spec.attribute("tiff:compression", quality)
56+
57+
58+
def numpy_to_image(
59+
buf: np.ndarray,
60+
save_path: Path | str = None,
61+
quality: int = 100,
62+
quality_other: Any = None,
63+
) -> ImageBuf:
64+
"""
65+
Convert a numpy array to an image.
66+
67+
Parameters
68+
----------
69+
buf : np.ndarray
70+
The numpy array to convert.
71+
path_path : Path or str
72+
The path to the image file to write. If not path is supplied the image will not be saved.
73+
quality : int, optional
74+
The quality of the image to save. Only applies if `save_path` is provided.
75+
76+
Returns
77+
-------
78+
ImageBuf
79+
The image.
80+
81+
"""
82+
image = ImageBuf(ImageSpec(buf.shape[1], buf.shape[0], buf.shape[2], oiio.FLOAT))
83+
image.set_pixels(ROI(0, buf.shape[1], 0, buf.shape[0]), buf)
84+
if save_path is not None:
85+
set_compression(
86+
image.spec(),
87+
Path(save_path).suffix,
88+
quality=quality,
89+
quality_other=quality_other,
90+
)
91+
image.write(str(save_path))
92+
return image
93+
94+
95+
def save_out_preview(item: bpy.types.ID):
96+
if item.preview:
97+
pixels = np.array(item.preview.image_pixels_float)
98+
99+
d3 = 4
100+
if len(pixels) == item.preview.image_size[0] * item.preview.image_size[1] * 3:
101+
d3 = 3
102+
pixels.resize(list(item.preview.image_size) + [d3])
103+
fp = (
104+
DIRECTORY
105+
/ f"{Path(bpy.data.filepath).stem}_{item.__class__.__name__.lower()}_{item.name}_preview.webp"
106+
)
107+
print(f"Saving preview for {item.name} to {fp}")
108+
numpy_to_image(
109+
pixels[::-1],
110+
save_path=fp,
111+
)
112+
113+
else:
114+
print(f"No preview found for {item.name}")
115+
116+
117+
if __name__ == "__main__":
118+
DIRECTORY.mkdir(parents=True, exist_ok=True)
119+
120+
if DO_WHOLE_FILE:
121+
for item in bpy.data.actions:
122+
if item.asset_data:
123+
save_out_preview(item)
124+
125+
for item in bpy.data.collections:
126+
if item.asset_data:
127+
save_out_preview(item)
128+
129+
for item in bpy.data.materials:
130+
if item.asset_data:
131+
save_out_preview(item)
132+
133+
for item in bpy.data.node_groups:
134+
if item.asset_data:
135+
save_out_preview(item)
136+
137+
for item in bpy.data.objects:
138+
if item.asset_data:
139+
save_out_preview(item)
140+
141+
for item in bpy.data.worlds:
142+
if item.asset_data:
143+
save_out_preview(item)
144+
else:
145+
item = getattr(bpy.data, ASSET_TYPE).get(ASSET_NAME)
146+
print(f"Getting item: Name:{ASSET_NAME}, Type:{ASSET_TYPE} | item: {item}")
147+
if item:
148+
save_out_preview(item)
149+
else:
150+
print(f"Item {ASSET_NAME} not found in {ASSET_TYPE}")

Diff for: ui/asset_browser.py

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def draw_single_asset(self, context: Context, layout: UILayout):
9494
col = row.column(align=True)
9595
col.operator("bkeeper.change_asset_icon", icon="FILE_FOLDER", text="")
9696
col.operator("bkeeper.rerender_thumbnail", icon="RESTRICT_RENDER_OFF", text="")
97+
if prefs.display_extras:
98+
col.operator("bkeeper.save_out_preview", icon="FILE_TICK", text="")
9799

98100
layout.separator()
99101

Diff for: utils.py

+24
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,30 @@ def rerender_thumbnail(
771771
subprocess.run(cmd)
772772
return None
773773

774+
def save_out_preview(self, directory: Path) -> None:
775+
"""Save out the preview image of the asset."""
776+
python_file = (
777+
Path(__file__).parent / "stand_alone_scripts" / "save_out_previews.py"
778+
)
779+
780+
args = [
781+
bpy.app.binary_path,
782+
"-b",
783+
"--factory-startup",
784+
str(self.orig_asset.full_library_path),
785+
"-P",
786+
str(python_file),
787+
str(directory),
788+
self.orig_asset.name,
789+
ASSET_TYPES_TO_ID_TYPES.get(self.id_type),
790+
"False",
791+
]
792+
793+
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
794+
if proc.returncode > 1:
795+
print(f"Error: {proc.stderr.decode()}")
796+
print(f"Output: {proc.stdout.decode()}")
797+
774798

775799
class Assets:
776800
def __init__(self, assets: list[AssetRepresentation]) -> None:

0 commit comments

Comments
 (0)