This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Режимы вывода изображения
Физика
Браузер SDK 2
Лицензирование и типы лицензий
Дополнения (Add-Ons)
Демонстрационные проекты
API Samples
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Контроль версий
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Player-ноды
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
C#
UnigineScript
Унифицированный язык шейдеров UUSL (Unified UNIGINE Shader Language)
Плагины
Форматы файлов
Материалы и шейдеры
Rebuilding the Engine Tools
Интерфейс пользователя (GUI)
Двойная точность координат
API
Animations-Related Classes
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
VR-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы
Внимание! Эта версия документация УСТАРЕЛА, поскольку относится к более ранней версии SDK! Пожалуйста, переключитесь на самую актуальную документацию для последней версии SDK.
Внимание! Эта версия документации описывает устаревшую версию SDK, которая больше не поддерживается! Пожалуйста, обновитесь до последней версии SDK.

Управление ассетами в плагинах редактора

This article contains information on working with assets in user plugins for UnigineEditor. Here you will find a brief description of the basic concepts and terms of the Asset System and learn how to manage resources (assets) in your custom tools based on UnigineEditor functionality.Эта статья содержит информацию о работе с ассетами в пользовательских плагинах для UnigineEditor. Здесь вы найдете краткое описание основных концепций и терминов Ассет-Системы и узнаете, как управлять ассетами в ваших пользовательских инструментах на основе функционала UnigineEditor.

See Also
Смотрите также#

Basic Concepts
Основные понятия#

Asset is the "unit of work" - any item that you can use within a project. Some assets are called native (i.e. can be used by the Engine "as is"), while others (non-native) require to be converted. The result of conversion is called a runtime file (or simply runtime). Ассет - это "единица работы" - любой элемент, который вы можете использовать в рамках проекта. Некоторые ассеты являются нативными (т.е. могут использоваться движком "как есть"), в то время как другие (ненативные) требуют преобразования (импорта). Результат преобразования называется runtime-файлом (или просто рантаймом).

For a single asset one or multiple runtimes can be generated, one of the runtimes is called primary (uniquely assoсiated with the asset, acting like an implied reference), while others are considered auxiliary (e.g., a model.fbx-asset produces multiple runtimes - .node, .mesh, .texture, .mat runtimes, but only the model.node is considered primary). When there is only one runtime produced it is considered as primary.Для одного ассета может быть сгенерировано один или несколько рантаймов, один из которых называется primary (основной, уникально связанный с ассетом, действует как подразумеваемая ссылка), в то время как другие считаются вспомогательными (например, model.fbx-ассет создает несколько рантаймов - .node, .mesh, .texture, .mat, но только model.node считается основным). Когда создается только один рантайм, он считается основным.

A metafile (.meta) is generated for each asset, contains important information about it: version, parameters, hash, runtime information, and a GUID (globally unique identifier) identifying its location in the project. GUIDs are used to refer to all assets and runtimes in the project instead of paths, giving you a lot of flexibility in managing resources, as you can rename or move assets and be sure that all links between them are kept.Для каждого ассета генерируется метафайл (.meta), содержащий важную информацию о нем: версию, параметры, хэш, информацию о рантаймах и GUID (globally unique identifier), определяющий его местоположение в проекте. Идентификаторы GUID используются для ссылки на все ассеты и рантаймы в проекте вместо путей, что дает большую гибкость в управлении ассетами, поскольку вы можете переименовывать или перемещать их и быть уверенным, что все ссылки между ними сохранены.

Normally all your project's resources (files, metafiles, runtime files) are stored in the project's data folder, but UNIGINE's virtual File System enables you to add links to external storage locations or packages via mount points. A mount point can be created via the Asset Browser (Create Mount Point) or via API. It is represented as a .umount file in the JSON format that stores a reference to an external directory or package as an absolute path or a path relative to the current directory. Also .umount file stores version of UNIGINE SDK in which the mount has been created, along with access information. All folders inside the mount point are treated by the File System as usual folders with assets, runtimes, and metafiles inside the data directory.Обычно все ассеты вашего проекта (файлы, метафайлы и рантаймы) хранятся в папке проекта data, но виртуальная файловая система UNIGINE позволяет добавлять ссылки на внешние хранилища или пакеты через точки монтирования. Точка монтирования может быть создана с помощью Asset Browser (Create Mount Point) или с помощью API. Он представлен в виде файла .umount в формате JSON, в котором хранится ссылка на внешний каталог или пакет в виде абсолютного пути или пути относительно текущего каталога. Также в файле .umount хранится версия UNIGINE SDK, в которой было создано монтирование, вместе с информацией о доступе. Все папки внутри точки монтирования обрабатываются файловой системой как обычные папки с ассетами, рантаймами и метафайлами внутри каталога data.

Asset Manager
Asset Manager#

Assets in UnigineEditor are managed... yes, by Asset Manager. It is a class that extends the functionality of the UNIGINE's File System. It enables you to check availability of a resource with a specific GUID, get its path, subscribe for the signals to perform certain actions when an asset is added, moved, updated, or deleted. AssetManager allows you to track assets changes in real time. You can modify your assets at any time after importing, the Asset System will notice when you save new changes to the file and will re-import it as necessary. In some cases it might be necessary to temporary change this behavior and block auto-refreshing (e.g. if you are building a custom integration with a version control system). You can do it using blockAutoRefresh() and unblockAutoRefresh() functions.Управление ассетами в UnigineEditor осуществляется через Asset Manager. Это класс, который расширяет функциональность File System UNIGINE. Он позволяет вам проверить доступность ассета с определенным GUID, получить его путь, подписаться на сигналы для выполнения определенных действий при добавлении, перемещении, обновлении или удалении ресурса. AssetManager позволяет отслеживать изменения ассетов в режиме реального времени. После импорта вы можете изменять ассеты в любое время, система ассетов заметит, когда вы сохраните изменения в файле, и повторно импортирует его при необходимости. В некоторых случаях может потребоваться временно изменить это поведение и заблокировать автоматическое обновление (например, если вы создаете пользовательскую интеграцию с системой контроля версий). Сделать это можно с помощью функций blockAutoRefresh() и unblockAutoRefresh().

AssetManager enables you to work with:AssetManager позволяет вам работать с:

  • assets and their eventsАссетами и их событиями
  • directories and their eventsКаталогами и их событиями
  • mount pointsТочками монтирования
  • file system watchersСмотрителями Файловой системы
  • process eventsСобытиями обработки
  • utilsУтилитами

The UnigineEditor API for AssetManager is presented here.API UnigineEditor для AssetManager представлен здесь.

AssetsPlugin
Плагин AssetsPlugin#

Most likely your custom tools that you develop on the basis of UnigineEditor's functionality will somehow manage assets. For your convenience there is a sample plugin demonstrating implementation of basic asset management operations using the AssetManager class. It is called AssetsPlugin and can be used as a template for your tool.Скорее всего, ваши пользовательские инструменты, которые вы разрабатываете на основе функционала UnigineEditor, так или иначе будут управлять ассетами. Для вашего удобства в SDK включен пример плагина, демонстрирующий реализацию базовых операций по управлению ассетами с использованием класса AssetManager. Он называется AssetsPlugin и может быть использован в качестве шаблона для вашего собственного инструментария.

Assets Plugin

Assets PluginПлагин Assets

Implemented Functions Реализованные функции

This plugin template implements the basic functionality of the Asset Manager. The plugin interacts with the Asset Manager system and adds a widget to Window Manager. AssetsPlugin allows you to:Этот шаблон плагина реализует базовый функционал Asset Manager. Плагин взаимодействует с системой Asset Manager и добавляет виджет в Window Manager. AssetsPlugin позволяет вам:

Import New Assets Synchronously
Синхронный импорт нового ассета#

The type of operation is used when imported asset(s) is critically important for the current execution of the application. The importing process is executed in the main thread of the application. It represents a blocking system call and the function will not return until the operation is complete.Тип операции используется, когда импортированный ассет(ы) критически важен для текущего выполнения приложения. Процесс импорта выполняется в основном потоке приложения. Это представляет собой блокирующий системный вызов, и функция не вернет управление до тех пор, пока операция не будет завершена.

This operation is implemented in the plugin as import_new_asset_synchronously() function.Эта операция реализована в плагине как функция import_new_asset_synchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method importing a new asset and waiting for the process to complete (blocking the thread)
void AssetsPlugin::import_new_asset_synchronously()
{
	// calling an auxiliary function to create a new asset and import it, displaying information on imported asset on success, or an error notification
	String result = create_and_import_new_asset_synchronously();
	if (!result.empty())
	{
		success_notification(
			String::format("Asset has imported successfully: \"%s\"", result.get()));

		// set current Editor selection to the imported asset and print information about it
		select_asset(result);

		print_asset_information(result);
	}
	else
	{
		error_notification(String::format("Can't import asset: \"%s\"", result.get()));
	}
}

// Method creating and importing an asset synchronously (blocking the thread until the operation is completed)
String AssetsPlugin::create_and_import_new_asset_synchronously() const
{
	String result;

	// making a base path for a new asset to be imported
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_NEW_ASSETS,
		String::basename(TEXTURE_PATH));

	// asking  the Asset Manager to generate a unique 
	String new_texture_path = AssetManager::generateUniquePath(base_asset_path);
	if (!create_asset(TEXTURE_PATH, new_texture_path))
	{
		return result;
	}
	// waiting for the texture asset to be imported by the AssetManager and returning the result (new texture path on success of an empty string on failure)
	if (AssetManager::importAssetSync(new_texture_path))
	{
		result = std::move(new_texture_path);
	}

	return result;
}

Import New Assets Asynchronously
Асинхронный импорт нового ассета#

This type of operation is used when imported asset(s) is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. adding a library of textures or models to be used later by the user). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue.Этот тип операции используется, когда импортированный ассет(ы) не является критически важным для текущего выполнения приложения и может быть выполнен в фоновом режиме рабочего процесса (например, добавление библиотеки текстур или моделей для последующего использования пользователем). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as import_new_asset_asynchronously() function.Эта операция реализована в плагине как функция import_new_asset_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method importing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::import_new_asset_asynchronously()
{
	// making a base target path for the asset and generating a unique virtual path for it
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_NEW_ASSETS,
		String::basename(TEXTURE_PATH));
	const String &new_texture_path = AssetManager::generateUniquePath(base_asset_path);
	if (!create_asset(TEXTURE_PATH, new_texture_path))
	{
		return;
	}

	// launching the importing process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::importAssetAsync(new_texture_path))
	{
		success_notification(String::format("Asset import process has started successfully: \"%s\"",
			new_texture_path.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset import process: \"%s\"", new_texture_path.get()));
	}
}

Remove Assets Asynchronously
Асинхронное удаление ассета#

This type of operation is used when removing of asset(s) is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. removing a pack of unused textures). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue.Этот тип операции используется, когда удаление ассетов не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, удаление пакета неиспользуемых текстур). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь.

Примечание
Removing the asset from a project means completely removing it from all of the project's elements referring to it. There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Удаление ресурса из проекта означает полное удаление его из всех элементов проекта, ссылающихся на него. Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as remove_asset_asynchronously() function.Эта операция реализована в плагине как функция remove_asset_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method removing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::remove_asset_asynchronously()
{
	// checking whether there are any assets in the source directory to remove (if not, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to remove: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// launching the asset removal process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	const String &asset_path_to_remove = asset_paths.last();
	if (AssetManager::removeAssetAsync(asset_path_to_remove))
	{
		success_notification(String::format("Asset removal process has started successfully: \"%s\"",
			asset_path_to_remove.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset removal process: \"%s\"", asset_path_to_remove.get()));
	}
}

Move Assets Asynchronously
Асинхронное перемещение ассета#

This type of operation is used when moving asset(s) is not critically important for the current execution of the application and can be performed in the background of the working process. It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue. It is used to manage files. All virtual paths of assets are kept protected.Этот тип операции используется, когда перемещение ассетов не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса. Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь. Он используется для управления файлами. Все виртуальные пути к ассетам защищены.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as move_asset_asynchronously() function.Эта операция реализована в плагине как функция move_asset_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method moving an asset to a new location in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::move_asset_asynchronously()
{
	// checking whether there are any assets in the source directory to move (if not, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to move: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// trying to create a destination directory and displaying error notification in case of failure
	if (!AssetManager::createDirectory(DIRECTORY_FOR_MOVED_ASSETS))
	{
		error_notification(
			String::format("Can't create directory: \"%s\"", DIRECTORY_FOR_MOVED_ASSETS));
		return;
	}

	// making a new asset path and generating a unique virtual path for it
	const String &asset_path_to_move = asset_paths.last();
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_MOVED_ASSETS,
		asset_path_to_move.basename());
	const String &new_asset_path = AssetManager::generateUniquePath(base_asset_path);

	// launching the "move asset" process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::moveAssetAsync(asset_path_to_move, new_asset_path))
	{
		success_notification(
			String::format("Asset moving process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_move.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset moving process: from \"%s\" to \"%s\"",
			asset_path_to_move.get(), new_asset_path.get()));
	}
}

Rename Assets Asynchronously
Асинхронное переименование ассета#

This type of operation is used when renaming asset(s) is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. adding a prefix for a set of textures in the library). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue. It is used to manage files. All virtual paths of assets are kept protected.Этот тип операции используется, когда переименование ассетов не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, добавление префикса для набора текстур в библиотеке). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь. Он используется для управления файлами. Все виртуальные пути к ассетам защищены.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as rename_asset_asynchronously() function.Эта операция реализована в плагине как функция rename_asset_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method renaming an asset asynchronously (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::rename_asset_asynchronously()
{
	// getting paths for all assets in the directory and checking whether it is empty (if so, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to rename: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// as an example, we rename the last file
	const String &asset_path_to_rename = asset_paths.last();

	const StringStack<> &new_asset_dir_path = String::dirname(asset_path_to_rename);
	const char *NEW_ASSET_FILE_NAME = "Renamed Asset";

	// making a new asset path and generating a unique virtual path for it
	StringStack<> new_asset_path = String::joinPaths(new_asset_dir_path, NEW_ASSET_FILE_NAME);
	new_asset_path += '.' + String::extension(asset_path_to_rename);

	new_asset_path = AssetManager::generateUniquePath(new_asset_path);

	// getting a base name for it to set while renaming
	const StringStack<> &new_asset_name = new_asset_path.basename();

	// putting a new "asset rename" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::renameAssetAsync(asset_path_to_rename, new_asset_name))
	{
		success_notification(
			String::format("Asset renaming process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_rename.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset renaming process: from \"%s\" to \"%s\"",
			asset_path_to_rename.get(), new_asset_path.get()));
	}
}

Copy Assets Asynchronously
Асинхронное копирование ассета#

This type of operation is used when copying asset(s) is not critically important for the current execution of the application and can be performed in the background of the working process. It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue. Can be used, for example, if the same asset with different dimensional parameters is required or to perform some tests with a given world file.Этот тип операции используется, когда копирование ассетов не является критически важным для текущего выполнения приложения и может выполняться в фоновом режиме рабочего процесса. Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь. Может использоваться, например, если требуется один и тот же ресурс с разными размерными параметрами или для выполнения некоторых тестов с заданным файлом world.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as copy_asset_asynchronously() function.Эта операция реализована в плагине как функция copy_asset_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method copying an asset asynchronously (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::copy_asset_asynchronously()
{
	// getting paths for all assets in the directory and checking whether it is empty (if so, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to copy: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// as an example, we copy the last file
	const String &asset_path_to_copy = asset_paths.last();

	// creating a new directory
	if (!AssetManager::createDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Can't create directory: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making a base path and generating a unique virtual path for a new copy of the asset
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_COPIED_ASSETS,
		asset_path_to_copy.basename());
	const String &new_asset_path = AssetManager::generateUniquePath(base_asset_path);

	// putting a new "asset copy" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::copyAssetAsync(asset_path_to_copy, new_asset_path))
	{
		success_notification(
			String::format("Asset copying process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_copy.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset copying process: from \"%s\" to \"%s\"",
			asset_path_to_copy.get(), new_asset_path.get()));
	}
}

Print Paths and GUIDs of All Known Assets
Вывод путей и GUID всех известных ресурсов#

Printing out the list of paths and GUIDs for all assets in the root project directory (including all subdirectories). This can be used to iterate over the assets in your project providing information on assets location.Вывод списка путей и идентификаторов GUID для всех ресурсов в корневом каталоге проекта (включая все подкаталоги). Это можно использовать для перебора всех ассетов в вашем проекте, и получения информации об их местоположении.

This operation is implemented in the plugin as print_all_known_assets() function.Эта операция реализована в плагине как функция print_all_known_assets().

AssetsPlugin.cpp

Исходный код (C++)
// Method printing out the list of paths and GUIDs for all assets in the root project directory (including all subdirectories)
void AssetsPlugin::print_all_known_assets()
{
	Log::message("[Assets Plugin] All asset paths:\n");
	for (const String &path : AssetManager::getAssetPaths())
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n[Assets Plugin] All asset GUIDs:\n");
	for (const UGUID &guid : AssetManager::getAssetGUIDs())
	{
		Log::message("\t%s\n", guid.getFileSystemString());
	}
	Log::message("\n");
}

Print Full Information About The Asset
Вывод полной информации об ассете#

Printing out general information for the specified asset path including virtual path, file GUID, access mode, and information about runtimes generated for this asset (GUID, type, alias, and file GUID). You may need this information, for example, when building a dependency graph for your assets.Вывод полной общей информации для указанного пути к ассету, включая виртуальный путь, GUID файла, режим доступа и информацию о рантайме, сгенерированном для этого ассета (идентификатор GUID, тип, псевдоним и GUID файла). Вам может понадобиться эта информация, например, при построении графика зависимостей для ваших ассетов.

This operation is implemented in the plugin as print_asset_information() function.Эта операция реализована в плагине как функция print_asset_information().

AssetsPlugin.cpp

Исходный код (C++)
// Method printing out general information for the specified asset path
void AssetsPlugin::print_asset_information(const char *asset_path)
{
	const UGUID &asset_guid = AssetManager::getAssetGUIDFromPath(asset_path);
	// It's only to demonstrate the usage of some API functions:
	Log::message("[Assets Plugin] Asset information:\n");
	Log::message("\tVirtual path: %s\n", AssetManager::getAssetPathFromGUID(asset_guid).get());
	Log::message("\tFile GUID: %s\n", asset_guid.getFileSystemString());
	Log::message("\tAccess: %s\n",
		AssetManager::isAssetWritable(asset_path) ? "read & write" : "read-only");
	auto get_import_parameters_as_string = [asset_path] {
		const CollectionPtr &parameters = AssetManager::getAssetImportParameters(asset_path);
		return convert_vector_to_flat_string(parameters->getNames());
	};
	Log::message("\tImport parameter names: %s\n", get_import_parameters_as_string().get());
 
	// getting the list of GUIDs for all runtimes generated for the asset path and displaying general information about them (if any)
	const Vector<UGUID> &runtime_guids = AssetManager::getRuntimeGUIDs(asset_path);
	if (runtime_guids.empty())
	{
		Log::message("\tNo runtimes\n");
	}
	else
	{
		Log::message("\tRuntimes:\n");
		for (int i = 0, count = runtime_guids.size(); i < count; ++i)
		{
			const UGUID &rt_guid = runtime_guids[i];
			Log::message("\t\tRuntime #%d is %s: Alias (%s) File GUID (%s)\n", i,
				AssetManager::isRuntimePrimary(rt_guid) ? "primary" : "non-primary",
				AssetManager::getRuntimeAlias(rt_guid).get(), rt_guid.getFileSystemString());
		}
	}
}

Create a New Directory
Создание нового каталога#

New directory creation, used to manage files within the project.Создание нового каталога, может использоваться для управления файлами внутри проекта.

This operation is implemented in the plugin as create_directory() function.Эта операция реализована в плагине как функция create_directory().

AssetsPlugin.cpp

Исходный код (C++)
// Method creating a new directory
void AssetsPlugin::create_directory()
{
	// making a base path for a new directory and generating a unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// creating a new directory for the specified path and displaying the corresponding notification
	if (AssetManager::createDirectory(new_path))
	{
		success_notification(
			String::format("Directory has been created successfully: \"%s\"", new_path.get()));
		assert(AssetManager::isDirectoryWritable(new_path));
	}
	else
	{
		error_notification(String::format("Can't create directory: \"%s\"", new_path.get()));
	}
}

Remove the Directory with Copied Assets Asynchronously
Асинхронное удаление каталога со скопированными ассетами#

This type of operation is used when removing of the directory is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. to remove a group of assets from the project). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue.Этот тип операции используется, когда удаление каталога не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, для удаления группы ассетов из проекта). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь.

Примечание
Removing a directory containing assets from a project means completely removing all these assets from all other project's elements referring to them. There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Удаление каталога, содержащего ассеты, из проекта означает полное удаление всех этих ассетов из всех других элементов проекта, ссылающихся на них. Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as remove_directory_with_copied_assets_asynchronously() function.Эта операция реализована в плагине как функция remove_directory_with_copied_assets_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method removing a directory with assets (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::remove_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// putting a new "remove directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::removeDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS))
	{
		success_notification(String::format("Directory removal process has started successfully: \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS));
	}
	else
	{
		error_notification(
			String::format("Can't start directory removal process: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
	}
}

Move the Directory with Copied Assets Asynchronously
Асинхронное перемещение каталога со скопированными ассетами#

This type of operation is used when moving of the directory is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. to move a group of assets to another location). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue.Этот тип операции используется, когда перемещение каталога не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, для перемещения группы ассетов в другое местоположение). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as move_directory_with_copied_assets_asynchronously() function.Эта операция реализована в плагине как функция move_directory_with_copied_assets_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method moving a directory with assets to a new location (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::move_directory_with_copied_assets_asynchronously()
{
	// checking if the source directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making a base destination path and generating a unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// putting a new "move directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::moveDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_path))
	{
		success_notification(
			String::format("Directory moving process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory moving process:  from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

Rename the Directory with Copied Assets Asynchronously
Асинхронное переименование каталога со скопированными ассетами#

This type of operation is used when renaming of the directory is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. to move a group of assets to another location). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue. It is used to manage files. All virtual paths of assets are kept protected.Этот тип операции используется, когда переименование каталога не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, для перемещения группы ресурсов в другое местоположение). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь. Он используется для управления файлами. Все виртуальные пути к ассетам защищены.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as rename_directory_with_copied_assets_asynchronously() function.Эта операция реализована в плагине как функция rename_directory_with_copied_assets_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method renaming a directory with assets (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::rename_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// generating a unique virtual path for the destination directory and getting its base name to change it
	const String &new_path = AssetManager::generateUniquePath(DIRECTORY_FOR_COPIED_ASSETS);
	const StringStack<> &new_name = new_path.basename();

	// putting a new "rename directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::renameDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_name))
	{
		success_notification(
			String::format("Directory renaming process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory renaming process: from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

Copy the Directory with Copied Assets Asynchronously
Асинхронное копирование каталога со скопированными ассетами#

This type of operation is used when copying of the directory is not critically important for the current execution of the application and can be performed in the background of the working process (e.g. to move a group of assets to another location). It represents a non-blocking call, the function returns immediately as soon as the operation is put to a queue. It is used to manage files. All virtual paths of assets are kept protected.Этот тип операции используется, когда копирование каталога не является критически важным для текущего выполнения приложения и может быть выполнено в фоновом режиме рабочего процесса (например, для перемещения группы ресурсов в другое местоположение). Он представляет собой неблокирующий вызов, функция возвращает управление немедленно, сразу после постановки операции в очередь. Он используется для управления файлами. Все виртуальные пути к ассетам защищены.

Примечание
There is only a notification about the start of the process, no information about its status or any errors occurred. To catch completion of the operation subscribe for an event via AssetManager::getEventProcessEnd() method.Есть только уведомление о начале процесса, никакой информации о его статусе или каких-либо возникших ошибках нет. Чтобы перехватить завершение операции, подпишитесь на событие с помощью метода AssetManager::getEventProcessEnd().

This operation is implemented in the plugin as copy_directory_with_copied_assets_asynchronously() function.Эта операция реализована в плагине как функция copy_directory_with_copied_assets_asynchronously().

AssetsPlugin.cpp

Исходный код (C++)
// Method copying a directory with assets to the specified path (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::copy_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making the destination base path, to which the directory is to be copied, and generating a new unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// launching the copying process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::copyDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_path))
	{
		success_notification(
			String::format("Directory copying process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory copying process: from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

Print All Known Directory Paths
Вывод всех известные путей к каталогам#

Lists all directory paths known to the Asset Manager, this can be used to iterate over all directories in the project. The operation is implemented in the plugin as print_all_known_directories() function.Перечисляет все пути к каталогам, известные Asset Manager, это можно использовать для перебора всех каталогов в проекте. Операция реализована в плагине как функция print_all_known_directories().

AssetsPlugin.cpp

Исходный код (C++)
// Method listing all directories known to the AssetManager
void AssetsPlugin::print_all_known_directories()
{
	Log::message("\n[Assets Plugin] All directory paths:\n");
	for (const String &path : AssetManager::getDirectoryPathsAll())
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n");
}

Print All Directory Paths for Specified Directory
Вывод всех путей подкаталогов для указанного каталога#

Lists all subdirectories for the specified directory. Can be used to iterate over subdirectories of the specified directory (e.g. when processing a well-structured pack of assets sorted by folders according to their types, usage logic, or otherwise). This operation is implemented in the plugin as print_directories_from_test_directory() function.Выводит список всех подкаталогов для указанного каталога. Может использоваться для перебора подкаталогов указанного каталога (например, при обработке хорошо структурированного пакета ассетов, отсортированных по папкам в соответствии с их типами, логикой использования или иным образом). Эта операция реализована в плагине как функция print_directories_from_test_directory().

AssetsPlugin.cpp

Исходный код (C++)
// Method listing all subdirectories of the specified path, that are known to the AssetManager
void AssetsPlugin::print_directories_from_test_directory()
{
	Log::message("\n[Assets Plugin] Directory paths from \"%s\":\n", DIRECTORY_FOR_NEW_DIRECTORIES);
	for (const String &path : AssetManager::getDirectoryPaths(DIRECTORY_FOR_NEW_DIRECTORIES))
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n");
}

Create a Mount Point
Создание точки монтирования#

This operation can be used to add a link to an external folder, shared storage or package (e.g. a library of 3D models, VFX, etc.) to the project.Эта операция может быть использована для добавления ссылки на внешнюю папку, общее хранилище или пакет (например, библиотеку 3D-моделей, VFX и т.д.) в проект.

The parameters of the mount point are managed via an instance of the MountPointParameters class - first, we should create an instance and specify necessary settings (path, access mode, etc.) and then tell the AssetManager to create a mount point via the createMountPoint() method. A corresponding .umount file is created in the project's data folder. This is a file in the JSON format that represents the root mount point.Управление параметрами точки монтирования осуществляется через экземпляр класса MountPointParameters - сначала мы должны создать экземпляр и указать необходимые настройки (путь, режим доступа и т.д.), а затем указать AssetManager создать точку монтирования с помощью метода createMountPoint(). Соответствующий файл .umount создается в папке data проекта. Это файл в формате JSON, который представляет корневую точку монтирования.

This operation is implemented in the plugin as create_mount_point() function.Эта операция реализована в плагине как функция create_mount_point().

AssetsPlugin.cpp

Исходный код (C++)
// Method creating a new mount point
void AssetsPlugin::create_mount_point()
{
	// checking if a path for a mount point to be created already exists and displaying the corresponding notifications
	if (AssetManager::isExist(DIRECTORY_FOR_MOUNT_POINT))
	{
		if (AssetManager::isAsset(DIRECTORY_FOR_MOUNT_POINT))
		{
			error_notification(
				String::format("Target path already contains a known asset: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else if (AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
		{
			assert(AssetManager::isDirectory(DIRECTORY_FOR_MOUNT_POINT));	// Mount point is a directory
			success_notification(
				String::format("Mount point for the target path already exists: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else if (AssetManager::isDirectory(DIRECTORY_FOR_MOUNT_POINT))
		{
			error_notification(
				String::format("Target path already contains a known directory: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else
		{
			// May be some reserved file or directory already exists there
			error_notification(
				String::format("Target path already exists and may contain a reserved file or some directory: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		return;
	}

	// making an absolute path to the mount
	StringStack<> abs_dir_path = String::normalizeDirPath(Engine::get()->getDataPath());
	abs_dir_path = abs_dir_path.trimLast("/");
	abs_dir_path = abs_dir_path.dirname();
	abs_dir_path = String::joinPaths(abs_dir_path, MOUNT_POINT_DIRECTORY_NAME);

	// getting an existing directory for the absolute path or creating it
	if (!Dir::mkdir(abs_dir_path))
	{
		error_notification(String::format("Can't create directory \"%s\"", abs_dir_path.get()));
		return;
	}

	// creating an instance of the MountPointParameters class to specify necessary settings for a new mount point to be created (path and access mode) 
	MountPointParametersPtr mount_params = MountPointParameters::create();
	mount_params->setAbsolutePath(abs_dir_path);
	mount_params->setAccess(MountPointParameters::ACCESS_READWRITE);

	// trying to create a new mount point for the specified path and parameters, displaying a notification depending on the result
	if (AssetManager::createMountPoint(DIRECTORY_FOR_MOUNT_POINT, mount_params))
	{
		success_notification(String::format("Mount point has been created successfully: \"%s\"",
			DIRECTORY_FOR_MOUNT_POINT));
		assert(AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT));
	}
	else
	{
		error_notification(
			String::format("Can't create mount point: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
	}
}

Remove a Mount Point
Удаление точки монтирования#

This operation can be used to disconnect from a shared storage.Эта операция может быть использована для отключения от общего хранилища.

Примечание
Removing a mount point containing assets used in the project means completely removing all these assets from all other project's elements referring to them.Удаление точки монтирования, содержащей ассеты, используемые в проекте, означает полное удаление всех этих ассетов из всех других элементов проекта, ссылающихся на них.

This operation is implemented in the plugin as remove_mount_point() function.Эта операция реализована в плагине как функция remove_mount_point().

AssetsPlugin.cpp

Исходный код (C++)
// Method removing a mount point
void AssetsPlugin::remove_mount_point()
{
	// checking if the specified mount point exists
	if (!AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		error_notification(
			String::format("Mount point doesn't exist \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		return;
	}

	// trying to remove the specified mount point via the AssetManager, displaying a notification depending on the result
	if (AssetManager::removeMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		success_notification(String::format("Mount point has been removed successfully: \"%s\"",
			DIRECTORY_FOR_MOUNT_POINT));
	}
	else
	{
		error_notification(
			String::format("Can't remove mount point: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
	}
}

Print Mount Point Information
Вывод информации о точке монтирования#

This operation can be used to obtain information about a mount point (virtual path, absolute path, access, filters). The parameters of the mount point are managed via an instance of the MountPointParameters class - first, we call the getMountPointParameters() method to fill this instance with necessary data and then use the instance to print information. This is a sample code demonstrating the use of public API.Эта операция может быть использована для получения информации о точке монтирования (виртуальный путь, абсолютный путь, доступ, фильтры). Управление параметрами точки монтирования осуществляется через экземпляр класса MountPointParameters - сначала мы вызываем метод getMountPointParameters(), чтобы заполнить этот экземпляр необходимыми данными, а затем используем экземпляр для печати информации. Это пример кода, демонстрирующий использование общедоступного API.

This operation is implemented in the plugin as print_mount_point_information() function.Эта операция реализована в плагине как функция print_mount_point_information().

AssetsPlugin.cpp

Исходный код (C++)
// Method printing complete information about the mount point (virtual path, access, and filters)
void AssetsPlugin::print_mount_point_information()
{
	// checking if the specified mount point exists
	if (!AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		Log::message("[Assets Plugin] Mount point doesn't exist: \"%s\"\n",
			DIRECTORY_FOR_MOUNT_POINT);
		return;
	}

	Log::message("[Assets Plugin] Mount point information\n\n");
	Log::message("\tVirtual path: \"%s\"\n", DIRECTORY_FOR_MOUNT_POINT);

	// retrieving parameters of the specified mount point to a MountPointParameters class instance and print information using its data
	const MountPointParametersPtr mount_params = AssetManager::getMountPointParameters(
		DIRECTORY_FOR_MOUNT_POINT);

	Log::message("\tAbsolute path: \"%s\"\n", mount_params->getAbsolutePath());
	Log::message("\tAccess: \"%s\"\n",
		(mount_params->getAccess() == MountPointParameters::ACCESS_READONLY) ? "read-only"
																			 : "read & write");

	// retrieving exclusive filters of the specified mount point to a vector and printing its contents or a corresponding message in case filters are not set
	const Vector<String> &exclusive_filters = mount_params->getExclusiveFilters();
	if (exclusive_filters.empty())
	{
		Log::message("\tNo exclusive filters\n");
	}
	else
	{
		Log::message("\tExclusive filters: %s\n", convert_vector_to_flat_string(exclusive_filters));
	}

	// retrieving ignore filters of the specified mount point to a vector and printing its contents or a corresponding message in case filters are not set
	const Vector<String> &ignore_filters = mount_params->getIgnoreFilters();
	if (ignore_filters.empty())
	{
		Log::message("\tNo ignore filters\n");
	}
	else
	{
		Log::message("\tIgnore filters: %s\n", convert_vector_to_flat_string(ignore_filters));
	}

	Log::message("\n");
}

Reimport Selected Landscape Map
Повторный импорт выбранного Landscape Map ассета#

This opreation reimports the selected Landscape Map asset after changing its import parameters. Can be used to reimport an asset existing in the project with a new set of import parameters.Эта операция повторно импортирует выбранный ассет Landscape Map после изменения его параметров импорта. Может использоваться для повторного импорта существующего в проекте ресурса с новым набором параметров.

This operation is implemented in the plugin as reimport_selected_landscape_map() function. This function uses the auxiliary function find_selected_lmap_file() which iterates over the current selection and finds the path to the .lmap asset to be reimported.Эта операция реализована в плагине как функция reimport_selected_landscape_map(). Эта функция использует вспомогательную функцию find_selected_lmap_file(), которая выполняет итерацию по текущему выбору и находит путь к ассету .lmap, подлежащему повторному экспорту.

AssetsPlugin.cpp

Исходный код (C++)
// Method reimporting the selected Landscape Map (.lmap) asset
void AssetsPlugin::reimport_selected_landscape_map()
{
	// trying to get an .lmap-file in the current selection
	const String &lmap_path = find_selected_lmap_file();
	if (lmap_path.empty())
	{
		return;
	}

	// getting current import parameters for the .lmap asset to an instance of the Collection class
	CollectionPtr source_import_parameters = AssetManager::getAssetImportParameters(lmap_path);
	LandscapeMapImportParametersPtr lmap_import_parameters = LandscapeMapImportParameters::create();
	lmap_import_parameters->load(source_import_parameters);

	// switching the data filling mode import parameter for the asset
	using DataMode = LandscapeMapImportParameters::DATA_FILLING_MODE;
	DataMode mode = lmap_import_parameters->getDataFillingMode();
	switch (mode)
	{
	case DataMode::DATA_FILLING_MODE_FROM_TILESET: mode = DataMode::DATA_FILLING_MODE_MANUAL; break;
	case DataMode::DATA_FILLING_MODE_MANUAL: mode = DataMode::DATA_FILLING_MODE_FROM_TILESET; break;
	}
	lmap_import_parameters->setDataFillingMode(mode);

	// saving new import parameters and using them to reimport asset asynchronously with corresponding notification on starting the process
	CollectionPtr dest_import_parameters = lmap_import_parameters->save();
	if (AssetManager::reimportAssetAsync(lmap_path, dest_import_parameters))
	{
		success_notification(String::format(
			"Asset reimport process has started successfully: \"%s\"", lmap_path.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset reimport process: \"%s\"", lmap_path.get()));
	}
}

// auxiliary method setting the current Editor selection to an asset having the specified path
void AssetsPlugin::select_asset(const char *asset_path)
{
	// checking if an asset with the specified path exists and getting its guid
	assert(AssetManager::isAsset(asset_path));
	const UGUID &asset_guid = AssetManager::getAssetGUIDFromPath(asset_path);
	if (asset_guid.isEmpty())
	{
		error_notification(String::format("Invalid path for asset: \"%s\"", asset_path));
		return;
	}

	// setting the current Editor selection to an asset with the specified guid
	SelectionAction::applySelection(SelectorGUIDs::createRuntimesSelector({asset_guid}));
}

// auxiliary method displaying a success message
void AssetsPlugin::success_notification(const char *message) const
{
	notification(message, Math::vec4_green);
}

// auxiliary method displaying an error message
void AssetsPlugin::error_notification(const char *message) const
{
	notification(message, Math::vec4_red);
}

// auxiliary method displaying a generic notification message using the specified color
void AssetsPlugin::notification(const char *message, const Math::vec4 &color) const
{
	if (label_notification_)
	{
		label_notification_->setFontColor(color);
		label_notification_->setText(message);
	}
	Log::message(color, "[Assets Plugin] %s\n", message);
}

// Auxiliary method creating an asset with the specified destination path for the specified source path
bool AssetsPlugin::create_asset(const char *source_path, const char *dest_path) const
{
	// retrieving a path to destination directory and creating it via the AssetManager (the corresponding handler shall be executed)
	const StringStack<> &dir_path = String::dirname(dest_path);
	if (!AssetManager::createDirectory(dir_path))
	{
		error_notification(String::format("Can't create directory: \"%s\"", dir_path.get()));
		return false;
	}

	// opening the source file for reading data
	FilePtr source_asset = File::create(source_path, "rb");
	if (!source_asset->isOpened())
	{
		error_notification(String::format("Can't open asset for reading: \"%s\"", source_path));
		return false;
	}

	// opening the destination asset file for writing data
	FilePtr dest_asset = File::create(dest_path, "wb");
	if (!dest_asset->isOpened())
	{
		error_notification(String::format("Can't open asset for writing: \"%s\"", dest_path));
		return false;
	}

	// declaring a 32Kb buffer for transferring data 
	const size_t MEMORY_SIZE = 1024 * 32;
	Vector<unsigned char> memory(MEMORY_SIZE);
	// reading and writing data from the source file to the destination asset
	while (0 == source_asset->eof())
	{
		const size_t bytes_read = source_asset->read(&memory[0], memory.size());
		const size_t bytes_written = dest_asset->write(&memory[0], bytes_read);
		if (bytes_read != bytes_written)
		{
			dest_asset->close();
			dest_asset.clear();
			Dir::remove(dest_path);
			error_notification(String::format("Can't write data to asset: \"%s\"", dest_path));
			return false;
		}
	}

	return true;
}

// Auxiliary method iterating over the current selection and finds the path to the first .lmap asset in the current Editor selection.
String AssetsPlugin::find_selected_lmap_file() const
{
	String lmap_path;

	// getting guids of runtimes for all currently selected assets, returning an empty string if nothing is selected
	const SelectorGUIDs *selector = Selection::getSelectorRuntimes();
	if (!selector || selector->empty())
	{
		return lmap_path;
	}

	// getting guids of all currently selected assets
	const Vector<UGUID> &selected_guids = selector->guids();
	for (const UGUID &guid : selected_guids)
	{
		// obtaining asset path by its GUID and checking if the path corresponds to a registered asset
		String virtual_path = AssetManager::getAssetPathFromGUID(guid);
		if (!AssetManager::isAsset(virtual_path))
		{
			Log::message("[Assets Plugin] Invalid asset path: %s\n", virtual_path.get());
			continue;
		}

		// getting an extension for the asset file and return the virtual path if it is an .lmap asset
		const StringStack<> &extension = virtual_path.extension().getLower();
		if (!String::equal(extension, "lmap"))
		{
			continue;
		}

		lmap_path = std::move(virtual_path);
		break;
	}

	return lmap_path;
}

// function to be executed on adding a new asset
void AssetsPlugin::asset_added(const char *path)
{
	Log::message("[Assets Plugin] Asset added: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed before removing an asset
void AssetsPlugin::asset_before_remove(const char *path)
{
	Log::message("[Assets Plugin] Asset before removing: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed after removing an asset
void AssetsPlugin::asset_removed(const char *path)
{
	Log::message("[Assets Plugin] Asset removed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on changing an asset
void AssetsPlugin::asset_changed(const char *path)
{
	Log::message("[Assets Plugin] Asset changed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on moving an asset to a new location
void AssetsPlugin::asset_moved(const char *path_from, const char *path_to)
{
	Log::message("[Assets Plugin] Asset moved from \"%s\" to \"%s\"\n", path_from, path_to);
	// Add your custom handling code here...
}

// function to be executed on adding a new directory
void AssetsPlugin::directory_added(const char *path)
{
	Log::message("[Assets Plugin] Directory added: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed before removing a directory
void AssetsPlugin::directory_before_remove(const char *path)
{
	Log::message("[Assets Plugin] Directory before removing: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed after removing a directory
void AssetsPlugin::directory_removed(const char *path)
{
	Log::message("[Assets Plugin] Directory removed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on moving a directory to a new location
void AssetsPlugin::directory_moved(const char *path_from, const char *path_to)
{
	Log::message("[Assets Plugin] Directory moved from \"%s\" to \"%s\"\n", path_from, path_to);
	// Add your custom handling code here...
}

// function to be executed on beginning of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
void AssetsPlugin::process_begin()
{
	Log::message("[Assets Plugin] Processing started\n");
	// Add your custom handling code here...
}

// function to be executed on completion of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
void AssetsPlugin::process_end()
{
	Log::message("[Assets Plugin] Processing completed\n");
	// Add your custom handling code here...
}

// function to be executed on changing selection in the Editor
void AssetsPlugin::selection_changed()
{
	// trying to get an .lmap-file in the current selection
	const String &lmap_path = find_selected_lmap_file();

	// updating the button text and status depending on the result
	const char BUTTON_LMAP_REIMPORT_NAME[] = "Reimport Landscape Map";
	const StringStack<> &button_name = String::format("%s%s%s", BUTTON_LMAP_REIMPORT_NAME,
		lmap_path.empty() ? "" : " - ", lmap_path.get());
	button_lmap_reimporting_->setText(button_name);
	button_lmap_reimporting_->setEnabled(lmap_path.size() > 0);
}

Complete Source Code
Полный исходный код#

The complete source code of the Assets plugin with comments is given below.Полный исходный код плагина Assets с комментариями приведен ниже.

AssetsPlugin.h

Исходный код (C++)
#pragma once

#include <editor/UniginePlugin.h>
#include <editor/UnigineEngineGuiWindow.h>

#include <UnigineVector.h>
#include <UnigineWidgets.h>

class QAction;

class AssetsPlugin final
	: public QObject
	, public ::UnigineEditor::Plugin
{
	Q_OBJECT
	Q_DISABLE_COPY(AssetsPlugin)
	Q_PLUGIN_METADATA(IID UNIGINE_EDITOR_PLUGIN_IID FILE "AssetsPlugin.json")
	Q_INTERFACES(UnigineEditor::Plugin)

public:
	AssetsPlugin() = default;

	bool init() override;
	void shutdown() override;

private:
	void activate();
	void deactivate();
	bool is_activated() const { return static_cast<bool>(window_); }

	void create_window();
	void destroy_window();
	// Auxiliary method creating the GUI of the Assets Plugin
	void build_gui();
	// Auxiliary method adding button elements to the GUI of the Assets Plugin
	void add_buttons(const Unigine::WidgetWindowPtr &main_window);
	// Auxiliary method setting up window title and update policy
	void setup_window();

	// Auxiliary method setting up handlers for various events (assets, directories, processing)
	void make_connections_to_asset_manager();
	// Auxiliary methods removing AssetManager event subscriptions
	void destroy_connections_from_asset_manager();

	// Auxiliary method subscribing for an event for tracking selection changes in the UnigineEditor
	void make_connection_to_selection();
	// Auxiliary methods removing added selection event subscriptions
	void destroy_connection_from_selection();

	// Method importing a new asset and waiting for the process to complete (blocking the thread)
	void import_new_asset_synchronously();
	// Method importing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
	void import_new_asset_asynchronously();
	// Method removing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
	void remove_asset_asynchronously();
	// Method moving an asset in the background (non-blocking, returns immediately without waiting for operation completion)
	void move_asset_asynchronously();
	// Method renaming an asset in the background (non-blocking, returns immediately without waiting for operation completion)
	void rename_asset_asynchronously();
	
	// Method copying an asset in the background (non-blocking, returns immediately without waiting for operation completion)
	void copy_asset_asynchronously();
	// Method printing out the list of paths and GUIDs for all assets in the root project directory (including all subdirectories)
	void print_all_known_assets();
	// Method printing out general information for the specified asset path
	void print_asset_information(const char *asset_path);

	// Method creating a new directory
	void create_directory();
	// Method removing a directory with assets (non-blocking, returns immediately without waiting for operation completion)
	void remove_directory_with_copied_assets_asynchronously();
	// Method moving a directory with assets to a new location (non-blocking, returns immediately without waiting for operation completion)
	void move_directory_with_copied_assets_asynchronously();
	// Method renaming a directory with assets (non-blocking, returns immediately without waiting for operation completion)
	void rename_directory_with_copied_assets_asynchronously();
	// Method copying a directory with assets to the specified path (non-blocking, returns immediately without waiting for operation completion)
	void copy_directory_with_copied_assets_asynchronously();
	// Method listing all directories known to the AssetManager
	void print_all_known_directories();
	// Method listing all subdirectories for the specified path, that are known to the AssetManager
	void print_directories_from_test_directory();

	// Method creating a new mount point
	void create_mount_point();
	// Method removing a mount point
	void remove_mount_point();
	// Method printing complete information about the mount point (virtual path, access, and filters)
	void print_mount_point_information();

	// Method reimporting the selected Landscape Map (.lmap) asset
	void reimport_selected_landscape_map();

	// Auxiliary method setting the current Editor selection to an asset having the specified path
	void select_asset(const char *asset_path);

	// Auxiliary method displaying a success message
	void success_notification(const char *message) const;
	// Auxiliary method displaying an error message
	void error_notification(const char *message) const;
	// Auxiliary method displaying a generic notification message using the specified color
	void notification(const char *message, const Unigine::Math::vec4 &color) const;

	// Method creating and importing an asset synchronously (blocking the thread until the operation is completed)
	Unigine::String create_and_import_new_asset_synchronously() const;
	// Auxiliary method creating an asset with the specified destination path for the specified source path
	bool create_asset(const char *source_path, const char *dest_path) const;
	// Auxiliary method iterating over the current selection and finds the path to the first .lmap asset in the current Editor selection.
	Unigine::String find_selected_lmap_file() const;

	// Event handlers from AssetManager:

	// Function to be executed on adding a new asset
	void asset_added(const char *path);
	// Function to be executed before removing an asset
	void asset_before_remove(const char *path);
	// Function to be executed after removing an asset
	void asset_removed(const char *path);
	// Function to be executed on changing an asset
	void asset_changed(const char *path);
	// Function to be executed on moving an asset to a new location
	void asset_moved(const char *path_from, const char *path_to);
	// Function to be executed on adding a new directory
	void directory_added(const char *path);
	// Function to be executed before removing a directory
	void directory_before_remove(const char *path);
	// Function to be executed after removing a directory
	void directory_removed(const char *path);
	// Function to be executed on moving a directory to a new location
	void directory_moved(const char *path_from, const char *path_to);
	// Function to be executed on beginning of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
	void process_begin();
	// Function to be executed on completion of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
	void process_end();

	// Event handler from Selection:

	// Function to be executed on changing selection in the Editor
	void selection_changed();

private:
	QAction *action_{};
	::UnigineEditor::EngineGuiWindow *window_{};
	Unigine::GuiPtr gui_;
	Unigine::WidgetLabelPtr label_notification_;
	Unigine::WidgetButtonPtr button_lmap_reimporting_;

	Unigine::EventConnections connections_;
	QMetaObject::Connection connection_selection_;
};

AssetsPlugin.cpp

Исходный код (C++)
#include "AssetsPlugin.h"

#include <UnigineDir.h>
#include <UnigineEngine.h>
#include <UnigineFileSystem.h>
#include <UnigineStreams.h>

#include <editor/UnigineActions.h>
#include <editor/UnigineAssetManager.h>
#include <editor/UnigineAssetImportParameters.h>
#include <editor/UnigineConstants.h>
#include <editor/UnigineWindowManager.h>
#include <editor/UnigineSelection.h>
#include <editor/UnigineSelector.h>

#include <QMenu>
#include <QTimer>

using namespace Unigine;
using namespace UnigineEditor;

namespace
{
// test directories to be used
const char DIRECTORY_FOR_NEW_ASSETS[] = "Assets Plugin - Tests/Textures";
const char DIRECTORY_FOR_MOVED_ASSETS[] = "Assets Plugin - Tests/Moved Assets";
const char DIRECTORY_FOR_COPIED_ASSETS[] = "Assets Plugin - Tests/Copied Assets";
const char DIRECTORY_FOR_NEW_DIRECTORIES[] = "Assets Plugin - Tests/Directories";
const char DIRECTORY_FOR_MOUNT_POINT[] = "Assets Plugin - Tests/Mount Point";

// name of the directory to be mounted
const char MOUNT_POINT_DIRECTORY_NAME[] = "Assets Plugin - Test Mount Point";

// path to a test asset
const char TEXTURE_PATH[] = "core/textures/world_light/celestial_body_moon.png";

String convert_vector_to_flat_string(const Vector<String> &v)
{
	String result;
	const int size = std::accumulate(v.begin(), v.end(), 0,
		[](int val, const String &s) { return val + s.size() + 3; });
	result.reserve(size + 1);
	for (const String &s : v)
	{
		result += "\"";
		result += s;
		result += "\" ";
	}
	return result;
}

} // anonymous namespace

bool AssetsPlugin::init()
{
	assert(AssetManager::isInitialized());

	// checking if the test asset file exists
	if (!FileSystem::isFileExist(TEXTURE_PATH))
	{
		Log::error("[Assets Plugin] The required resource is missing: %s\n", TEXTURE_PATH);
	}

	// adding an item for the Assets Plugin to the Windows menu of the UnigineEditor
	QMenu *menu = WindowManager::findMenu(Constants::MM_WINDOWS);
	action_ = menu->addAction("Assets Plugin", this, &AssetsPlugin::activate);

	connect(WindowManager::instance(), &WindowManager::windowHidden, this, [this](QWidget *w) {
		if (w == window_)
		{
			// Postpone destroying, otherwise other receivers will get a dead object
			QTimer::singleShot(0, this, &AssetsPlugin::deactivate);
		}
	});

	return true;
}

void AssetsPlugin::shutdown()
{
	disconnect(WindowManager::instance(), nullptr, this, nullptr);

	delete action_;
	action_ = nullptr;

	deactivate();
}

void AssetsPlugin::activate()
{
	if (is_activated())
	{
		WindowManager::show(window_);
		return;
	}

	create_window();
	make_connections_to_asset_manager();
	make_connection_to_selection();
}

void AssetsPlugin::deactivate()
{
	destroy_connection_from_selection();
	destroy_connections_from_asset_manager();
	destroy_window();
}

void AssetsPlugin::create_window()
{
	assert(nullptr == window_);
	window_ = new EngineGuiWindow;

	setup_window();
	build_gui();

	// To refresh button states
	selection_changed();

	WindowManager::add(window_, WindowManager::NEW_FLOATING_AREA);
	WindowManager::show(window_);
}

void AssetsPlugin::destroy_window()
{
	label_notification_.clear();
	button_lmap_reimporting_.clear();
	gui_.clear();

	if (window_)
	{
		connections_.disconnectAll();
		WindowManager::remove(window_);
		delete window_;
		window_ = nullptr;
	}
}

// Auxiliary method creating the GUI of the Assets Plugin
void AssetsPlugin::build_gui()
{
	gui_ = window_->getGui();

	WidgetWindowPtr main_window = WidgetWindow::create(gui_, "Assets Window");
	gui_->addChild(main_window, Gui::ALIGN_OVERLAP);

	constexpr int DEFAULT_WIDTH = 800;
	constexpr int DEFAULT_HEIGHT = 400;
	main_window->setWidth(DEFAULT_WIDTH);
	main_window->setHeight(DEFAULT_HEIGHT);
	main_window->setSizeable(true);

	add_buttons(main_window);

	label_notification_ = WidgetLabel::create(gui_);
	main_window->addChild(label_notification_, Gui::ALIGN_EXPAND);
}

// Auxiliary method adding button elements to the GUI of the Assets Plugin
void AssetsPlugin::add_buttons(const WidgetWindowPtr &main_window)
{
	auto create_button = [&main_window, this](const char *name, void (AssetsPlugin::*p)()) {
		WidgetButtonPtr button = WidgetButton::create(gui_, name);
		button->getEventClicked().connect(this, p);
		main_window->addChild(button);
		return button;
	};

	// assets
	create_button("Import new asset synchronously", &AssetsPlugin::import_new_asset_synchronously);
	create_button("Import new asset asynchronously",
		&AssetsPlugin::import_new_asset_asynchronously);
	create_button("Remove asset", &AssetsPlugin::remove_asset_asynchronously);
	create_button("Move asset", &AssetsPlugin::move_asset_asynchronously);
	create_button("Rename asset", &AssetsPlugin::rename_asset_asynchronously);
	create_button("Copy asset", &AssetsPlugin::copy_asset_asynchronously);
	create_button("Print all known assets", &AssetsPlugin::print_all_known_assets);
	create_button("Create directory", &AssetsPlugin::create_directory);
	create_button("Remove directory with copied assets",
		&AssetsPlugin::remove_directory_with_copied_assets_asynchronously);
	create_button("Move directory with copied assets",
		&AssetsPlugin::move_directory_with_copied_assets_asynchronously);
	create_button("Rename directory with copied assets",
		&AssetsPlugin::rename_directory_with_copied_assets_asynchronously);
	create_button("Copy directory with copied assets",
		&AssetsPlugin::copy_directory_with_copied_assets_asynchronously);
	create_button("Print all known directories", &AssetsPlugin::print_all_known_directories);
	StringStack<> button_name = String::format("Print all known directories from \"%s\"",
		DIRECTORY_FOR_NEW_DIRECTORIES);
	create_button(button_name, &AssetsPlugin::print_directories_from_test_directory);
	create_button("Create mount point", &AssetsPlugin::create_mount_point);
	create_button("Remove mount point", &AssetsPlugin::remove_mount_point);
	create_button("Print mount point information", &AssetsPlugin::print_mount_point_information);

	button_lmap_reimporting_ = create_button("", &AssetsPlugin::reimport_selected_landscape_map);
}

// Auxiliary method setting up window title and update policy
void AssetsPlugin::setup_window()
{
	window_->setWindowTitle("Plugin - Assets");
	// setting a policy to update window when it's visible to ensure that the status of the Landscape Map Reimport button is refreshed as we select an asset
	window_->setRenderingPolicy(EngineGuiWindow::RENDERING_POLICY_WINDOW_VISIBLE);
}

// Auxiliary method setting up handlers for various events (assets, directories, processing)
void AssetsPlugin::make_connections_to_asset_manager()
{
	using AM = AssetManager;

	assert(connections_.empty());
	// assets
	AM::getEventAssetAdded().connect(connections_, this, &AssetsPlugin::asset_added);
	AM::getEventAssetBeforeRemove().connect(connections_, this, &AssetsPlugin::asset_before_remove);
	AM::getEventAssetRemoved().connect(connections_, this, &AssetsPlugin::asset_removed);
	AM::getEventAssetChanged().connect(connections_, this, &AssetsPlugin::asset_changed);
	AM::getEventAssetMoved().connect(connections_, this, &AssetsPlugin::asset_moved);
	// directories
	AM::getEventDirectoryAdded().connect(connections_, this, &AssetsPlugin::directory_added);
	AM::getEventDirectoryBeforeRemove().connect(connections_, this, &AssetsPlugin::directory_before_remove);
	AM::getEventDirectoryRemoved().connect(connections_, this, &AssetsPlugin::directory_removed);
	AM::getEventDirectoryMoved().connect(connections_, this, &AssetsPlugin::directory_moved);
	// processing
	AM::getEventProcessBegin().connect(connections_, this, &AssetsPlugin::process_begin);
	AM::getEventProcessEnd().connect(connections_, this, &AssetsPlugin::process_end);
}

// Auxiliary method setting up an event handler for tracking selection changes in the UnigineEditor
void AssetsPlugin::make_connection_to_selection()
{
	Selection *selection = Selection::instance();
	connection_selection_ = connect(selection, &Selection::changed, this,
		&AssetsPlugin::selection_changed);
}

// Auxiliary methods removing event subscriptions
void AssetsPlugin::destroy_connection_from_selection()
{
	disconnect(connection_selection_);
}

void AssetsPlugin::destroy_connections_from_asset_manager()
{
	connections_.disconnectAll();
}

// Method importing a new asset and waiting for the process to complete (blocking the thread)
void AssetsPlugin::import_new_asset_synchronously()
{
	// calling an auxiliary function to create a new asset and import it, displaying information on imported asset on success, or an error notification
	String result = create_and_import_new_asset_synchronously();
	if (!result.empty())
	{
		success_notification(
			String::format("Asset has imported successfully: \"%s\"", result.get()));

		// set current Editor selection to the imported asset and print information about it
		select_asset(result);

		print_asset_information(result);
	}
	else
	{
		error_notification(String::format("Can't import asset: \"%s\"", result.get()));
	}
}

// Method importing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::import_new_asset_asynchronously()
{
	// making a base target path for the asset and generating a unique virtual path for it
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_NEW_ASSETS,
		String::basename(TEXTURE_PATH));
	const String &new_texture_path = AssetManager::generateUniquePath(base_asset_path);
	if (!create_asset(TEXTURE_PATH, new_texture_path))
	{
		return;
	}

	// launching the importing process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::importAssetAsync(new_texture_path))
	{
		success_notification(String::format("Asset import process has started successfully: \"%s\"",
			new_texture_path.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset import process: \"%s\"", new_texture_path.get()));
	}
}

// Method removing an asset in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::remove_asset_asynchronously()
{
	// checking whether there are any assets in the source directory to remove (if not, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to remove: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// launching the asset removal process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	const String &asset_path_to_remove = asset_paths.last();
	if (AssetManager::removeAssetAsync(asset_path_to_remove))
	{
		success_notification(String::format("Asset removal process has started successfully: \"%s\"",
			asset_path_to_remove.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset removal process: \"%s\"", asset_path_to_remove.get()));
	}
}

// Method moving an asset to a new location in the background (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::move_asset_asynchronously()
{
	// checking whether there are any assets in the source directory to move (if not, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to move: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// trying to create a destination directory and displaying error notification in case of failure
	if (!AssetManager::createDirectory(DIRECTORY_FOR_MOVED_ASSETS))
	{
		error_notification(
			String::format("Can't create directory: \"%s\"", DIRECTORY_FOR_MOVED_ASSETS));
		return;
	}

	// making a new asset path and generating a unique virtual path for it
	const String &asset_path_to_move = asset_paths.last();
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_MOVED_ASSETS,
		asset_path_to_move.basename());
	const String &new_asset_path = AssetManager::generateUniquePath(base_asset_path);

	// launching the "move asset" process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::moveAssetAsync(asset_path_to_move, new_asset_path))
	{
		success_notification(
			String::format("Asset moving process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_move.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset moving process: from \"%s\" to \"%s\"",
			asset_path_to_move.get(), new_asset_path.get()));
	}
}

// Method renaming an asset asynchronously (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::rename_asset_asynchronously()
{
	// getting paths for all assets in the directory and checking whether it is empty (if so, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to rename: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// as an example, we rename the last file
	const String &asset_path_to_rename = asset_paths.last();

	const StringStack<> &new_asset_dir_path = String::dirname(asset_path_to_rename);
	const char *NEW_ASSET_FILE_NAME = "Renamed Asset";

	// making a new asset path and generating a unique virtual path for it
	StringStack<> new_asset_path = String::joinPaths(new_asset_dir_path, NEW_ASSET_FILE_NAME);
	new_asset_path += '.' + String::extension(asset_path_to_rename);

	new_asset_path = AssetManager::generateUniquePath(new_asset_path);

	// getting a base name for it to set while renaming
	const StringStack<> &new_asset_name = new_asset_path.basename();

	// putting a new "asset rename" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::renameAssetAsync(asset_path_to_rename, new_asset_name))
	{
		success_notification(
			String::format("Asset renaming process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_rename.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset renaming process: from \"%s\" to \"%s\"",
			asset_path_to_rename.get(), new_asset_path.get()));
	}
}

// Method copying an asset asynchronously (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::copy_asset_asynchronously()
{
	// getting paths for all assets in the directory and checking whether it is empty (if so, report and return)
	const Vector<String> &asset_paths = AssetManager::getAssetPathsForDirectory(
		DIRECTORY_FOR_NEW_ASSETS);
	if (asset_paths.empty())
	{
		error_notification(String::format("There are no assets to copy: \"%s\"\n",
			DIRECTORY_FOR_NEW_ASSETS));
		return;
	}

	// as an example, we copy the last file
	const String &asset_path_to_copy = asset_paths.last();

	// creating a new directory
	if (!AssetManager::createDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Can't create directory: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making a base path and generating a unique virtual path for a new copy of the asset
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_COPIED_ASSETS,
		asset_path_to_copy.basename());
	const String &new_asset_path = AssetManager::generateUniquePath(base_asset_path);

	// putting a new "asset copy" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::copyAssetAsync(asset_path_to_copy, new_asset_path))
	{
		success_notification(
			String::format("Asset copying process has started successfully: from \"%s\" to \"%s\"",
				asset_path_to_copy.get(), new_asset_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start asset copying process: from \"%s\" to \"%s\"",
			asset_path_to_copy.get(), new_asset_path.get()));
	}
}

// Method printing out the list of paths and GUIDs for all assets in the root project directory (including all subdirectories)
void AssetsPlugin::print_all_known_assets()
{
	Log::message("[Assets Plugin] All asset paths:\n");
	for (const String &path : AssetManager::getAssetPaths())
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n[Assets Plugin] All asset GUIDs:\n");
	for (const UGUID &guid : AssetManager::getAssetGUIDs())
	{
		Log::message("\t%s\n", guid.getFileSystemString());
	}
	Log::message("\n");
}

// Method printing out general information for the specified asset path
void AssetsPlugin::print_asset_information(const char *asset_path)
{
	const UGUID &asset_guid = AssetManager::getAssetGUIDFromPath(asset_path);
	// It's only to demonstrate the usage of some API functions:
	Log::message("[Assets Plugin] Asset information:\n");
	Log::message("\tVirtual path: %s\n", AssetManager::getAssetPathFromGUID(asset_guid).get());
	Log::message("\tFile GUID: %s\n", asset_guid.getFileSystemString());
	Log::message("\tAccess: %s\n",
		AssetManager::isAssetWritable(asset_path) ? "read & write" : "read-only");
	auto get_import_parameters_as_string = [asset_path] {
		const CollectionPtr &parameters = AssetManager::getAssetImportParameters(asset_path);
		return convert_vector_to_flat_string(parameters->getNames());
	};
	Log::message("\tImport parameter names: %s\n", get_import_parameters_as_string().get());
 
	// getting the list of GUIDs for all runtimes generated for the asset path and displaying general information about them (if any)
	const Vector<UGUID> &runtime_guids = AssetManager::getRuntimeGUIDs(asset_path);
	if (runtime_guids.empty())
	{
		Log::message("\tNo runtimes\n");
	}
	else
	{
		Log::message("\tRuntimes:\n");
		for (int i = 0, count = runtime_guids.size(); i < count; ++i)
		{
			const UGUID &rt_guid = runtime_guids[i];
			Log::message("\t\tRuntime #%d is %s: Alias (%s) File GUID (%s)\n", i,
				AssetManager::isRuntimePrimary(rt_guid) ? "primary" : "non-primary",
				AssetManager::getRuntimeAlias(rt_guid).get(), rt_guid.getFileSystemString());
		}
	}
}

// Method creating a new directory
void AssetsPlugin::create_directory()
{
	// making a base path for a new directory and generating a unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// creating a new directory for the specified path and displaying the corresponding notification
	if (AssetManager::createDirectory(new_path))
	{
		success_notification(
			String::format("Directory has been created successfully: \"%s\"", new_path.get()));
		assert(AssetManager::isDirectoryWritable(new_path));
	}
	else
	{
		error_notification(String::format("Can't create directory: \"%s\"", new_path.get()));
	}
}

// Method removing a directory with assets (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::remove_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// putting a new "remove directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::removeDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS))
	{
		success_notification(String::format("Directory removal process has started successfully: \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS));
	}
	else
	{
		error_notification(
			String::format("Can't start directory removal process: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
	}
}

// Method moving a directory with assets to a new location (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::move_directory_with_copied_assets_asynchronously()
{
	// checking if the source directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making a base destination path and generating a unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// putting a new "move directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::moveDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_path))
	{
		success_notification(
			String::format("Directory moving process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory moving process:  from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

// Method renaming a directory with assets (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::rename_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// generating a unique virtual path for the destination directory and getting its base name to change it
	const String &new_path = AssetManager::generateUniquePath(DIRECTORY_FOR_COPIED_ASSETS);
	const StringStack<> &new_name = new_path.basename();

	// putting a new "rename directory" task to a queue and displaying the corresponding notification depending on the result
	if (AssetManager::renameDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_name))
	{
		success_notification(
			String::format("Directory renaming process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory renaming process: from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

// Method copying a directory with assets to the specified path (non-blocking, returns immediately without waiting for operation completion)
void AssetsPlugin::copy_directory_with_copied_assets_asynchronously()
{
	// checking if the specified directory exists (if not, report and return)
	if (!AssetManager::isDirectory(DIRECTORY_FOR_COPIED_ASSETS))
	{
		error_notification(
			String::format("Directory doesn't exist: \"%s\"", DIRECTORY_FOR_COPIED_ASSETS));
		return;
	}

	// making the destination base path, to which the directory is to be copied, and generating a new unique virtual path for it
	const StringStack<> &base_path = String::joinPaths(DIRECTORY_FOR_NEW_DIRECTORIES,
		"New Directory");
	const String &new_path = AssetManager::generateUniquePath(base_path);

	// launching the copying process in the background (asynchronously, without blocking the main thread) and displaying a notification, if it has started successfully
	// to catch completion of the operation set a handler via AssetManager::getEventProcessEnd.connect() method (see method "process_end()" here)
	if (AssetManager::copyDirectoryAsync(DIRECTORY_FOR_COPIED_ASSETS, new_path))
	{
		success_notification(
			String::format("Directory copying process has started successfully: from \"%s\" to \"%s\"",
				DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
	else
	{
		error_notification(String::format("Can't start directory copying process: from \"%s\" to \"%s\"",
			DIRECTORY_FOR_COPIED_ASSETS, new_path.get()));
	}
}

// Method listing all directories known to the AssetManager
void AssetsPlugin::print_all_known_directories()
{
	Log::message("\n[Assets Plugin] All directory paths:\n");
	for (const String &path : AssetManager::getDirectoryPathsAll())
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n");
}

// Method listing all subdirectories of the specified path, that are known to the AssetManager
void AssetsPlugin::print_directories_from_test_directory()
{
	Log::message("\n[Assets Plugin] Directory paths from \"%s\":\n", DIRECTORY_FOR_NEW_DIRECTORIES);
	for (const String &path : AssetManager::getDirectoryPaths(DIRECTORY_FOR_NEW_DIRECTORIES))
	{
		Log::message("\t%s\n", path.get());
	}
	Log::message("\n");
}

// Method creating a new mount point
void AssetsPlugin::create_mount_point()
{
	// checking if a path for a mount point to be created already exists and displaying the corresponding notifications
	if (AssetManager::isExist(DIRECTORY_FOR_MOUNT_POINT))
	{
		if (AssetManager::isAsset(DIRECTORY_FOR_MOUNT_POINT))
		{
			error_notification(
				String::format("Target path already contains a known asset: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else if (AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
		{
			assert(AssetManager::isDirectory(DIRECTORY_FOR_MOUNT_POINT));	// Mount point is a directory
			success_notification(
				String::format("Mount point for the target path already exists: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else if (AssetManager::isDirectory(DIRECTORY_FOR_MOUNT_POINT))
		{
			error_notification(
				String::format("Target path already contains a known directory: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		else
		{
			// May be some reserved file or directory already exists there
			error_notification(
				String::format("Target path already exists and may contain a reserved file or some directory: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		}
		return;
	}

	// making an absolute path to the mount
	StringStack<> abs_dir_path = String::normalizeDirPath(Engine::get()->getDataPath());
	abs_dir_path = abs_dir_path.trimLast("/");
	abs_dir_path = abs_dir_path.dirname();
	abs_dir_path = String::joinPaths(abs_dir_path, MOUNT_POINT_DIRECTORY_NAME);

	// getting an existing directory for the absolute path or creating it
	if (!Dir::mkdir(abs_dir_path))
	{
		error_notification(String::format("Can't create directory \"%s\"", abs_dir_path.get()));
		return;
	}

	// creating an instance of the MountPointParameters class to specify necessary settings for a new mount point to be created (path and access mode) 
	MountPointParametersPtr mount_params = MountPointParameters::create();
	mount_params->setAbsolutePath(abs_dir_path);
	mount_params->setAccess(MountPointParameters::ACCESS_READWRITE);

	// trying to create a new mount point for the specified path and parameters, displaying a notification depending on the result
	if (AssetManager::createMountPoint(DIRECTORY_FOR_MOUNT_POINT, mount_params))
	{
		success_notification(String::format("Mount point has been created successfully: \"%s\"",
			DIRECTORY_FOR_MOUNT_POINT));
		assert(AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT));
	}
	else
	{
		error_notification(
			String::format("Can't create mount point: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
	}
}

// Method removing a mount point
void AssetsPlugin::remove_mount_point()
{
	// checking if the specified mount point exists
	if (!AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		error_notification(
			String::format("Mount point doesn't exist \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
		return;
	}

	// trying to remove the specified mount point via the AssetManager, displaying a notification depending on the result
	if (AssetManager::removeMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		success_notification(String::format("Mount point has been removed successfully: \"%s\"",
			DIRECTORY_FOR_MOUNT_POINT));
	}
	else
	{
		error_notification(
			String::format("Can't remove mount point: \"%s\"", DIRECTORY_FOR_MOUNT_POINT));
	}
}

// Method printing complete information about the mount point (virtual path, access, and filters)
void AssetsPlugin::print_mount_point_information()
{
	// checking if the specified mount point exists
	if (!AssetManager::isMountPoint(DIRECTORY_FOR_MOUNT_POINT))
	{
		Log::message("[Assets Plugin] Mount point doesn't exist: \"%s\"\n",
			DIRECTORY_FOR_MOUNT_POINT);
		return;
	}

	Log::message("[Assets Plugin] Mount point information\n\n");
	Log::message("\tVirtual path: \"%s\"\n", DIRECTORY_FOR_MOUNT_POINT);

	// retrieving parameters of the specified mount point to a MountPointParameters class instance and print information using its data
	const MountPointParametersPtr mount_params = AssetManager::getMountPointParameters(
		DIRECTORY_FOR_MOUNT_POINT);

	Log::message("\tAbsolute path: \"%s\"\n", mount_params->getAbsolutePath());
	Log::message("\tAccess: \"%s\"\n",
		(mount_params->getAccess() == MountPointParameters::ACCESS_READONLY) ? "read-only"
																			 : "read & write");

	// retrieving exclusive filters of the specified mount point to a vector and printing its contents or a corresponding message in case filters are not set
	const Vector<String> &exclusive_filters = mount_params->getExclusiveFilters();
	if (exclusive_filters.empty())
	{
		Log::message("\tNo exclusive filters\n");
	}
	else
	{
		Log::message("\tExclusive filters: %s\n", convert_vector_to_flat_string(exclusive_filters));
	}

	// retrieving ignore filters of the specified mount point to a vector and printing its contents or a corresponding message in case filters are not set
	const Vector<String> &ignore_filters = mount_params->getIgnoreFilters();
	if (ignore_filters.empty())
	{
		Log::message("\tNo ignore filters\n");
	}
	else
	{
		Log::message("\tIgnore filters: %s\n", convert_vector_to_flat_string(ignore_filters));
	}

	Log::message("\n");
}

// Method reimporting the selected Landscape Map (.lmap) asset
void AssetsPlugin::reimport_selected_landscape_map()
{
	// trying to get an .lmap-file in the current selection
	const String &lmap_path = find_selected_lmap_file();
	if (lmap_path.empty())
	{
		return;
	}

	// getting current import parameters for the .lmap asset to an instance of the Collection class
	CollectionPtr source_import_parameters = AssetManager::getAssetImportParameters(lmap_path);
	LandscapeMapImportParametersPtr lmap_import_parameters = LandscapeMapImportParameters::create();
	lmap_import_parameters->load(source_import_parameters);

	// switching the data filling mode import parameter for the asset
	using DataMode = LandscapeMapImportParameters::DATA_FILLING_MODE;
	DataMode mode = lmap_import_parameters->getDataFillingMode();
	switch (mode)
	{
	case DataMode::DATA_FILLING_MODE_FROM_TILESET: mode = DataMode::DATA_FILLING_MODE_MANUAL; break;
	case DataMode::DATA_FILLING_MODE_MANUAL: mode = DataMode::DATA_FILLING_MODE_FROM_TILESET; break;
	}
	lmap_import_parameters->setDataFillingMode(mode);

	// saving new import parameters and using them to reimport asset asynchronously with corresponding notification on starting the process
	CollectionPtr dest_import_parameters = lmap_import_parameters->save();
	if (AssetManager::reimportAssetAsync(lmap_path, dest_import_parameters))
	{
		success_notification(String::format(
			"Asset reimport process has started successfully: \"%s\"", lmap_path.get()));
	}
	else
	{
		error_notification(
			String::format("Can't start asset reimport process: \"%s\"", lmap_path.get()));
	}
}

// auxiliary method setting the current Editor selection to an asset having the specified path
void AssetsPlugin::select_asset(const char *asset_path)
{
	// checking if an asset with the specified path exists and getting its guid
	assert(AssetManager::isAsset(asset_path));
	const UGUID &asset_guid = AssetManager::getAssetGUIDFromPath(asset_path);
	if (asset_guid.isEmpty())
	{
		error_notification(String::format("Invalid path for asset: \"%s\"", asset_path));
		return;
	}

	// setting the current Editor selection to an asset with the specified guid
	SelectionAction::applySelection(SelectorGUIDs::createRuntimesSelector({asset_guid}));
}
// auxiliary method displaying a success message
void AssetsPlugin::success_notification(const char *message) const
{
	notification(message, Math::vec4_green);
}

// auxiliary method displaying an error message
void AssetsPlugin::error_notification(const char *message) const
{
	notification(message, Math::vec4_red);
}

// auxiliary method displaying a generic notification message using the specified color
void AssetsPlugin::notification(const char *message, const Math::vec4 &color) const
{
	if (label_notification_)
	{
		label_notification_->setFontColor(color);
		label_notification_->setText(message);
	}
	Log::message(color, "[Assets Plugin] %s\n", message);
}
// Method creating and importing an asset synchronously (blocking the thread until the operation is completed)
String AssetsPlugin::create_and_import_new_asset_synchronously() const
{
	String result;

	// making a base path for a new asset to be imported
	const StringStack<> &base_asset_path = String::joinPaths(DIRECTORY_FOR_NEW_ASSETS,
		String::basename(TEXTURE_PATH));

	// asking  the Asset Manager to generate a unique 
	String new_texture_path = AssetManager::generateUniquePath(base_asset_path);
	if (!create_asset(TEXTURE_PATH, new_texture_path))
	{
		return result;
	}
	// waiting for the texture asset to be imported by the AssetManager and returning the result (new texture path on success of an empty string on failure)
	if (AssetManager::importAssetSync(new_texture_path))
	{
		result = std::move(new_texture_path);
	}

	return result;
}
// Auxiliary method creating an asset with the specified destination path for the specified source path
bool AssetsPlugin::create_asset(const char *source_path, const char *dest_path) const
{
	// retrieving a path to destination directory and creating it via the AssetManager (the corresponding handler shall be executed)
	const StringStack<> &dir_path = String::dirname(dest_path);
	if (!AssetManager::createDirectory(dir_path))
	{
		error_notification(String::format("Can't create directory: \"%s\"", dir_path.get()));
		return false;
	}

	// opening the source file for reading data
	FilePtr source_asset = File::create(source_path, "rb");
	if (!source_asset->isOpened())
	{
		error_notification(String::format("Can't open asset for reading: \"%s\"", source_path));
		return false;
	}

	// opening the destination asset file for writing data
	FilePtr dest_asset = File::create(dest_path, "wb");
	if (!dest_asset->isOpened())
	{
		error_notification(String::format("Can't open asset for writing: \"%s\"", dest_path));
		return false;
	}

	// declaring a 32Kb buffer for transferring data 
	const size_t MEMORY_SIZE = 1024 * 32;
	Vector<unsigned char> memory(MEMORY_SIZE);
	// reading and writing data from the source file to the destination asset
	while (0 == source_asset->eof())
	{
		const size_t bytes_read = source_asset->read(&memory[0], memory.size());
		const size_t bytes_written = dest_asset->write(&memory[0], bytes_read);
		if (bytes_read != bytes_written)
		{
			dest_asset->close();
			dest_asset.clear();
			Dir::remove(dest_path);
			error_notification(String::format("Can't write data to asset: \"%s\"", dest_path));
			return false;
		}
	}

	return true;
}

// Auxiliary method iterating over the current selection and finds the path to the first .lmap asset in the current Editor selection.
String AssetsPlugin::find_selected_lmap_file() const
{
	String lmap_path;

	// getting guids of runtimes for all currently selected assets, returning an empty string if nothing is selected
	const SelectorGUIDs *selector = Selection::getSelectorRuntimes();
	if (!selector || selector->empty())
	{
		return lmap_path;
	}

	// getting guids of all currently selected assets
	const Vector<UGUID> &selected_guids = selector->guids();
	for (const UGUID &guid : selected_guids)
	{
		// obtaining asset path by its GUID and checking if the path corresponds to a registered asset
		String virtual_path = AssetManager::getAssetPathFromGUID(guid);
		if (!AssetManager::isAsset(virtual_path))
		{
			Log::message("[Assets Plugin] Invalid asset path: %s\n", virtual_path.get());
			continue;
		}

		// getting an extension for the asset file and return the virtual path if it is an .lmap asset
		const StringStack<> &extension = virtual_path.extension().getLower();
		if (!String::equal(extension, "lmap"))
		{
			continue;
		}

		lmap_path = std::move(virtual_path);
		break;
	}

	return lmap_path;
}

// function to be executed on adding a new asset
void AssetsPlugin::asset_added(const char *path)
{
	Log::message("[Assets Plugin] Asset added: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed before removing an asset
void AssetsPlugin::asset_before_remove(const char *path)
{
	Log::message("[Assets Plugin] Asset before removing: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed after removing an asset
void AssetsPlugin::asset_removed(const char *path)
{
	Log::message("[Assets Plugin] Asset removed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on changing an asset
void AssetsPlugin::asset_changed(const char *path)
{
	Log::message("[Assets Plugin] Asset changed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on moving an asset to a new location
void AssetsPlugin::asset_moved(const char *path_from, const char *path_to)
{
	Log::message("[Assets Plugin] Asset moved from \"%s\" to \"%s\"\n", path_from, path_to);
	// Add your custom handling code here...
}

// function to be executed on adding a new directory
void AssetsPlugin::directory_added(const char *path)
{
	Log::message("[Assets Plugin] Directory added: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed before removing a directory
void AssetsPlugin::directory_before_remove(const char *path)
{
	Log::message("[Assets Plugin] Directory before removing: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed after removing a directory
void AssetsPlugin::directory_removed(const char *path)
{
	Log::message("[Assets Plugin] Directory removed: %s\n", path);
	// Add your custom handling code here...
}

// function to be executed on moving a directory to a new location
void AssetsPlugin::directory_moved(const char *path_from, const char *path_to)
{
	Log::message("[Assets Plugin] Directory moved from \"%s\" to \"%s\"\n", path_from, path_to);
	// Add your custom handling code here...
}

// function to be executed on beginning of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
void AssetsPlugin::process_begin()
{
	Log::message("[Assets Plugin] Processing started\n");
	// Add your custom handling code here...
}

// function to be executed on completion of asynchronous asset processing by the Asset Manager (e.g., validation, import, reimport, removal, copying, conversion, migration etc.)
void AssetsPlugin::process_end()
{
	Log::message("[Assets Plugin] Processing completed\n");
	// Add your custom handling code here...
}

// function to be executed on changing selection in the Editor
void AssetsPlugin::selection_changed()
{
	// trying to get an .lmap-file in the current selection
	const String &lmap_path = find_selected_lmap_file();

	// updating the button text and status depending on the result
	const char BUTTON_LMAP_REIMPORT_NAME[] = "Reimport Landscape Map";
	const StringStack<> &button_name = String::format("%s%s%s", BUTTON_LMAP_REIMPORT_NAME,
		lmap_path.empty() ? "" : " - ", lmap_path.get());
	button_lmap_reimporting_->setText(button_name);
	button_lmap_reimporting_->setEnabled(lmap_path.size() > 0);
}
Последнее обновление: 16.08.2024
Build: ()