238 lines
7.6 KiB
Python
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)
|