Files
blender-portable-repo/extensions/user_default/memsaver_personal/preferences.py
T
2026-03-17 14:30:01 -06:00

238 lines
7.6 KiB
Python

# copyright (c) 2018- polygoniq xyz s.r.o.
import typing
import bpy
import os
import tempfile
import shutil
from . import polib
telemetry = polib.get_telemetry("memsaver")
MODULE_CLASSES: typing.List[typing.Type] = []
IMAGE_SIZES_ENUM = [
("32", "32", "32"),
("64", "64", "64"),
("128", "128", "128"),
("256", "256", "256"),
("512", "512", "512"),
("1024", "1024", "1024"),
("2048", "2048", "2048"),
("4096", "4096", "4096"),
("8192", "8192", "8192"),
("16384", "16384", "16384"),
]
@polib.log_helpers_bpy.logged_operator
class PackLogs(bpy.types.Operator):
bl_idname = "memsaver.pack_logs"
bl_label = "Pack Logs"
bl_description = "Archives polygoniq logs as zip file and opens its location"
bl_options = {'REGISTER'}
def execute(self, context):
packed_logs_directory_path = polib.log_helpers_bpy.pack_logs(telemetry)
polib.utils_bpy.xdg_open_file(packed_logs_directory_path)
return {'FINISHED'}
MODULE_CLASSES.append(PackLogs)
@polib.log_helpers_bpy.logged_operator
class OpenCacheFolder(bpy.types.Operator):
bl_idname = "memsaver.open_cache_folder"
bl_label = "Open Cache Folder"
bl_description = "Opens the directory with cached derivative images"
bl_options = {'REGISTER'}
def execute(self, context):
prefs = get_preferences(context)
polib.utils_bpy.xdg_open_file(prefs.get_cache_path())
return {'FINISHED'}
MODULE_CLASSES.append(OpenCacheFolder)
@polib.log_helpers_bpy.logged_preferences
class Preferences(bpy.types.AddonPreferences):
bl_idname = __package__
cache_path: bpy.props.StringProperty(
name="Cache Path",
default=os.path.expanduser("~/memsaver_cache"),
description="Where derivatives (of various sizes) of images will be cached.",
subtype='DIR_PATH',
update=lambda self, context: polib.utils_bpy.absolutize_preferences_path(
self, context, "cache_path"
),
)
# Used to choose target of image sizer operator
operator_target: bpy.props.EnumProperty(
name="Target",
description="Choose to what objects/images the operator should apply to",
items=[
('SELECTED_OBJECTS', "Selected Objects", "All selected objects"),
('SCENE_OBJECTS', "Scene Objects", "All objects in current scene"),
('ALL_OBJECTS', "All Objects", "All objects in the .blend file"),
(None),
(
'ALL_IMAGES_EXCEPT_HDR_EXR',
"All Images except HDR and EXR",
"All images except HDR and EXR, even those not used in any objects",
),
(
'ALL_HDR_EXR_IMAGES',
"All HDR and EXR Images",
"All HDR and EXR images, even those not used in any objects",
),
('ALL_IMAGES', "All Images", "All images, even those not used in any objects"),
],
default='SCENE_OBJECTS',
)
adaptive_image_enabled: bpy.props.BoolProperty(
name="Optimize Images",
default=True,
)
adaptive_image_quality_factor: bpy.props.FloatProperty(
name="Quality Factor",
description="Object 2D bounds are multiplied by this to figure out image side size. \n\n"
"Texture sizes are calculated based on how many pixels the objects that use them take "
"either horizontally or vertically in the camera view. For example if an object is bigger "
"vertically and it takes 10% of a FHD resolution then it takes 108 px and so its textures "
"(if not used anywhere else) have to be at least 256px if the quality factor is set to "
"2.00. Please note that if a big texture is mapped onto an object in a way that only "
"a fraction of it is actually used, the downscaling might have a detrimental effect since "
"even though the object is let's say 970 px wide it uses only a small part of the entire "
"texture which then gets downscaled based on the bounds and not based on actual mapping "
"density",
default=1.0,
min=0.001,
)
adaptive_image_minimum_size: bpy.props.EnumProperty(
name="Minimum Image Size",
description="Minimal image size, the algorithm will not choose a smaller size than this",
items=IMAGE_SIZES_ENUM,
default="256",
)
adaptive_image_maximum_size: bpy.props.EnumProperty(
name="Maximum Image Size",
description="Maximal image size, the algorithm will not choose a larger size than this",
items=IMAGE_SIZES_ENUM,
default="4096", # 4k
)
adaptive_mesh_enabled: bpy.props.BoolProperty(
name="Decimate Meshes",
default=False,
)
adaptive_mesh_full_quality_distance: bpy.props.FloatProperty(
name="Full Quality Max Distance",
description="We won't apply any decimation on meshes closer to the camera than this value",
default=20.0,
min=0.0,
)
adaptive_mesh_lowest_quality_distance: bpy.props.FloatProperty(
name="Lowest Quality Distance",
description="Distance at which we will apply the lowest quality decimation",
default=200.0,
min=0.0,
)
adaptive_mesh_lowest_quality_decimation_ratio: bpy.props.FloatProperty(
name="Lowest Quality Decimation Ratio",
description="Which decimation ratio do we apply at the lowest quality distance",
default=0.2,
min=0.0,
max=1.0,
)
adaptive_mesh_lowest_face_count: bpy.props.IntProperty(
name="Lowest Face Count",
description="Ignore meshes with face count lower than this value",
default=5000,
min=0,
)
adaptive_animation_mode: bpy.props.BoolProperty(
name="Animation Mode",
description="Instead of figuring out distances from current frame, consider all frames",
default=False,
)
overlay_text_size_px: bpy.props.FloatProperty(
name="Overlay Text Size",
description="Size of the overlay text in pixels",
default=16.0,
min=1.0,
)
overlay_text_color: bpy.props.FloatVectorProperty(
name="Overlay Text Color",
description="Color of the text overlay. "
"Useful when the default value would blend with background",
min=0.0,
max=1.0,
default=(1.0, 1.0, 1.0, 1.0),
size=4,
subtype='COLOR',
)
change_size_desired_size: bpy.props.EnumProperty(
name="Desired Size",
description="Desired Maximum Side Size of Textures",
items=IMAGE_SIZES_ENUM + [("CUSTOM", "CUSTOM", "CUSTOM")],
default="2048",
)
change_size_custom_size: bpy.props.IntProperty(
name="Custom Size",
description="Desired Maximum Side Size of Textures",
default=3072,
)
def get_cache_path(self) -> str:
if not os.path.isdir(self.cache_path):
os.makedirs(self.cache_path)
return self.cache_path
def draw(self, context: bpy.types.Context):
row = self.layout.row()
row.prop(self, "cache_path")
row = self.layout.row()
row.operator(OpenCacheFolder.bl_idname, icon='FILE_CACHE')
row = self.layout.row()
row.operator(PackLogs.bl_idname, icon='EXPERIMENTAL')
polib.ui_bpy.draw_settings_footer(self.layout)
MODULE_CLASSES.append(Preferences)
def get_preferences(context: bpy.types.Context) -> Preferences:
return context.preferences.addons[__package__].preferences
def register():
for cls in MODULE_CLASSES:
bpy.utils.register_class(cls)
def unregister():
for cls in reversed(MODULE_CLASSES):
bpy.utils.unregister_class(cls)