# #### 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 2 # 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, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### import os import re import bpy from ..modules.poliigon_core.api_remote_control_params import ( CATEGORY_ALL, get_search_key, KEY_TAB_IMPORTED) from ..modules.poliigon_core.assets import ( AssetData, AssetType, ModelType) from ..modules.poliigon_core.multilingual import _t from ..operators.operator_material import set_op_mat_disp_strength # TODO(SOFT-2421): Deactivated as it seems to have unwanted side effects. # from .dlg_assets import _draw_button_quick_preview from .utils_dlg import ( check_convention, get_model_op_details, safe_size_apply) from .. import reporting def show_quick_menu( cTB, asset_data: AssetData, hide_detail_view: bool = False) -> None: """Generates the quick options menu next to an asset in the UI grid.""" asset_type_data = asset_data.get_type_data() asset_name = asset_data.asset_name asset_id = asset_data.asset_id asset_type = asset_data.asset_type credits = 0 if asset_data.credits is None else asset_data.credits is_free = credits == 0 # Configuration if asset_data.is_purchased: # If downloading and already purchased. title = _t("Choose Texture Size") else: title = asset_name in_scene = False sizes = asset_type_data.get_size_list(local_only=False) downloaded = asset_type_data.get_size_list( local_only=True, addon_convention=cTB._asset_index.addon_convention, local_convention=asset_data.get_convention(local=True)) key = get_search_key( tab=KEY_TAB_IMPORTED, search="", category_list=[CATEGORY_ALL]) query_key = cTB._asset_index._query_key_to_tuple( key, chunk=-1, chunk_size=1000000) if query_key not in cTB._asset_index.cached_queries: # The request is probably still be in flight in_scene = False elif asset_id in cTB._asset_index.cached_queries[query_key]: in_scene = True prefer_blend = cTB.settings["download_prefer_blend"] link_blend = cTB.link_blend_session blend_exists = False fbx_exists = False if asset_type == AssetType.MODEL: blend_exists = asset_type_data.has_mesh( model_type=ModelType.BLEND, native_only=True, renderer=None) # None is for legacy cycles models w/o engine name fbx_exists = asset_type_data.has_mesh( model_type=ModelType.FBX, native_only=False, renderer="") any_model = blend_exists or fbx_exists is_linked_blend_import = prefer_blend and link_blend and blend_exists def _imported_model_extras( context, layout: bpy.types.UILayout) -> None: area = cTB.settings["area"] if area != KEY_TAB_IMPORTED or asset_type != AssetType.MODEL: return op = layout.operator( "poliigon.poliigon_select", text=_t("Select"), icon="RESTRICT_SELECT_OFF", ) op.mode = "model" op.data = asset_name op.tooltip = _t("{0}\n(Select all instances)").format(asset_name) layout.separator() @reporting.handle_draw() def draw(self, context): layout = self.layout _imported_model_extras(context, layout) # List the different resolution sizes to provide. have_size_opts = asset_data.is_purchased or is_free or cTB.is_unlimited_user() have_size_opts &= not asset_data.state.has_error if have_size_opts: for size in sizes: if asset_type == AssetType.TEXTURE: draw_material_sizes(context, size, layout) elif asset_type == AssetType.MODEL: draw_model_sizes(context, size, layout) elif asset_type == AssetType.HDRI: draw_hdri_sizes(context, size, layout) else: label = _t("{0} not implemented yet").format(asset_type) layout.label(text=label) # TODO(SOFT-2421): Deactivated as it seems to have unwanted side effects. # else: # _draw_button_quick_preview( # cTB, # layout_row=layout, # asset_data=asset_data, # is_selection=True, # have_text_label=True # ) # If else branch is activated, the following separator needs to be # outside if/else layout.separator() op = layout.operator( "poliigon.open_preferences", text=_t("Open Import options in Preferences"), icon="PREFERENCES", ) op.set_focus = "show_default_prefs" layout.separator() # Always show view online and high res previews. if not hide_detail_view: if bpy.app.version >= (4, 2): row_detail_view = layout.row() row_detail_view.operator_context = 'INVOKE_DEFAULT' op = row_detail_view.operator( "poliigon.detail_view", text=_t("View Asset Details"), icon="OUTLINER_OB_IMAGE", ) else: op = layout.operator( "poliigon.view_thumbnail", text=_t("View Large Preview"), icon="OUTLINER_OB_IMAGE", ) op.asset_id = asset_data.asset_id op = layout.operator( "poliigon.poliigon_link", text=_t("View online"), icon_value=cTB.ui_icons["ICON_poliigon"].icon_id, ) op.mode = str(asset_id) op.tooltip = _t("View on Poliigon.com") # If already local, support opening the folder location. if not downloaded: return op = layout.operator( "poliigon.poliigon_folder", text=_t("Open folder location"), icon="FILE_FOLDER") op.asset_id = asset_id # ... and provide option to sync with asset browser # TODO(Andreas): Asset Browser integration and AssetIndex # in_asset_browser = asset_data.get("in_asset_browser", False) in_asset_browser = asset_data.runtime.is_in_asset_browser() is_feature_avail = bpy.app.version >= (3, 0) missing_local_model = asset_type == AssetType.MODEL and not any_model if not is_feature_avail or missing_local_model: return # Asset browser sync option client_starting = cTB.lock_client_start.locked() owned = cTB.check_if_purchased(asset_data.asset_id) layout.separator() row = layout.row() msg = _t("Synchronize with Asset Browser") if not owned: msg = _t("Must own to sync with Asset Browser") elif in_asset_browser: msg = _t("Already synced with Asset Browser") elif client_starting: msg = _t("Starting Asset Browser sync...") if cTB.user_legacy_own_assets(): op = row.operator( "poliigon.update_asset_browser", text=msg, icon="FILE_REFRESH") op.asset_id = asset_id row.enabled = not in_asset_browser and not client_starting and owned def draw_material_sizes( context, size: str, layout: bpy.types.UILayout) -> None: """Draw the menu row for a materials' single resolution size.""" row = layout.row() imported = f"{asset_name}_{size}" in bpy.data.materials if asset_data.get_convention() >= 1: all_expected_maps_for_size = asset_type_data.all_expected_maps_local( cTB.user.map_preferences, size) else: all_expected_maps_for_size = size in downloaded if imported or all_expected_maps_for_size: # Action: Load and apply it if imported: label = _t("{0} (apply material)").format(size) tip = _t("Apply {0} Material\n{1}").format(size, asset_name) elif context.selected_objects: label = _t("{0} (import + apply)").format(size) tip = _t("Apply {0} Material\n{1}").format(size, asset_name) else: label = _t("{0} (import)").format(size) tip = _t("Import {0} Material\n{1}").format(size, asset_name) # If nothing is selected and this size is already importing, # then there's nothing to do. if imported and not context.selected_objects: row.enabled = False op = row.operator( "poliigon.poliigon_material", text=label, icon="TRACKING_REFINE_BACKWARDS") # Order is relevant here. vType needs to be set before vSize! op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) op.mapping = "UV" op.scale = 1.0 op.use_16bit = cTB.settings["use_16"] op.reuse_material = True op.tooltip = tip set_op_mat_disp_strength(op, asset_name, op.mode_disp) else: # Action: Download # (for free assets this is purchase + implicit auto-download) if check_convention(asset_data): label = _t("{0} (download)").format(size) else: label = _t("{size} (Update needed)").format(size) row.enabled = False op = row.operator( "poliigon.poliigon_download", text=label, icon="IMPORT") op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) if is_free and not asset_data.is_purchased: op.mode = "purchase" else: op.mode = "download" op.tooltip = _t("Download {0} Material\n{1}").format( size, asset_name) def draw_model_sizes( context, size: str, layout: bpy.types.UILayout) -> None: """Draw the menu row for a model's single resolution size.""" row = layout.row() if size in downloaded and any_model: # Action: Load and apply it lod, label, tip = get_model_op_details( cTB, asset_data, size) if is_linked_blend_import: label += _t(" (disable link .blend to import size)") op = row.operator( "poliigon.poliigon_model", text=label, icon="TRACKING_REFINE_BACKWARDS") op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) op.tooltip = tip op.lod = lod if len(lod) > 0 else "NONE" row.enabled = not is_linked_blend_import else: # Action: Download if check_convention(asset_data): label = _t("{0} (download)").format(size) else: label = _t("{0} (Update needed)").format(size) row.enabled = False op = row.operator( "poliigon.poliigon_download", text=label, icon="IMPORT") op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) if is_free and not asset_data.is_purchased: op.mode = "purchase" else: op.mode = "download" op.tooltip = _t("Download {0} textures\n{1}").format( size, asset_name) def draw_hdri_sizes( context, size: str, layout: bpy.types.UILayout) -> None: """Draw the menu row for an HDRI's single resolution size.""" row = layout.row() size_light = "" if in_scene: image_name_light = asset_name + "_Light" if image_name_light in bpy.data.images.keys(): path_light = bpy.data.images[image_name_light].filepath filename = os.path.basename(path_light) match_object = re.search(r"_(\d+K)[_\.]", filename) size_light = match_object.group(1) if match_object else cTB.settings["hdri"] if size in downloaded: # Action: Load and apply it if size == size_light: label = _t("{0} (apply HDRI)").format(size) tip = _t("Apply {0} HDRI\n{1}").format(size, asset_name) else: label = _t("{0} (import HDRI)").format(size) tip = _t("Import {0} HDRI\n{1}").format(size, asset_name) op = row.operator( "poliigon.poliigon_hdri", text=label, icon="TRACKING_REFINE_BACKWARDS") op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) if cTB.settings["hdri_use_jpg_bg"]: size_bg = cTB.settings["hdrib"] size_bg = asset_type_data.get_size( size_bg, local_only=True, addon_convention=cTB.addon_convention, local_convention=asset_data.get_convention(local=True)) op.size_bg = f"{size_bg}_JPG" else: op.size_bg = f"{size}_EXR" op.tooltip = tip else: # Action: Download if check_convention(asset_data): label = _t("{0} (download)").format(size) else: label = _t("{0} (Update needed)").format(size) row.enabled = False op = row.operator( "poliigon.poliigon_download", text=label, icon="IMPORT") op.asset_id = asset_id safe_size_apply(cTB, op, size, asset_name) if is_free and not asset_data.is_purchased: op.mode = "purchase" else: op.mode = "download" op.tooltip = _t("Download {0}\n{1}").format(size, asset_name) # Generate the popup menu. bpy.context.window_manager.popup_menu(draw, title=title, icon="QUESTION")