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
Учебные материалы

Создание вашего первого плагина для редактора

This article describes how to create an Editor plugin and get the first impression of what a plugin consists of and what its general structure is.В этой статье описывается, как создать плагин Editor и получить первое представление о том, из чего состоит плагин и какова его общая структура.

Внимание
This feature is an experimental one and is not recommended for production use.Данный функционал является экспериментальным и не рекомендуется для промышленного использования.

UnigineEditor is an application written entirely in C++ relying a lot on the Qt5 framework infrastructure. So, in order to extend its functionality not only C++ programming skills are required, but you should also be familiar with the Qt5 framework, and CMake build system (especially if you use Linux).UnigineEditor — это приложение, полностью написанное на C++, в значительной степени полагающееся на инфраструктуру фреймворка Qt5. Таким образом, для расширения его функциональности требуются не только навыки программирования на C++, но и знакомство с фреймворком Qt5 и системой сборки CMake (особенно если вы используете Linux).

Примечание
Qt Framework version 5.12.3 is required to develop plugins for UnigineEditor. More information on setting up development environment for this purpose is available in this article.Для разработки плагинов для UnigineEditor требуется Qt Framework версии 5.12.3. Более подробная информация о настройке среды разработки для этой цели доступна в этой статье .

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

Plugin Templates
Шаблоны плагинов#

For your convenience there are three project templates (Engine GUI Window, Assets, and Materials) that you can use as a basis to create your own plugin. Below you'll find a brief description of each template.Для вашего удобства есть два шаблона проекта (Engine GUI Window и Materials), которые вы можете использовать в качестве основы для создания собственного плагина. Ниже вы найдете краткое описание каждого шаблона.

Materials Plugin
Плагин Materials#

This plugin template implements the basic functionality of the Editor's Materials Hierarchy window (see the picture below).Этот шаблон плагина реализует базовую функциональность окна редактора Materials Hierarchy (см. рисунок ниже).

Примечание
Only Release version of this plugin template is available.Доступна только версия Release этого шаблона плагина.

Materials Plugin

Materials PluginПлагин материалов

Basically the plugin's architecture looks as follows:В целом, архитектура плагина выглядит следующим образом:

Materials Plugin Architecture

Materials PluginПлагин материалов

The MaterialsPlugin class is an initialization point and complete representation of the plugin. It creates necessary data model and via the base interface associates it with the widget, which displays the hierarchy. The model directly interacts with the Materials Manager and transforms information on materials to the required structure.Класс MaterialsPlugin — это точка инициализации и полное представление плагина. Создает необходимую модель данных и через базовый интерфейс связывает ее с виджетом, отображающим иерархию. Модель напрямую взаимодействует с Materials Manager и преобразует информацию о материалах в требуемую структуру.

The plugin interacts with Engine Materials System and Editor's external subsystems:Плагин взаимодействует с системой материалов Engine и внешними подсистемами редактора:

  • Adds a widget to WindowManager.добавляет виджет в WindowManager;
  • Adds a new item to the main menu bar.добавляет новый пункт в строку главного меню;
  • Updates the state of materials widget when the global selection is changed.обновляет состояние виджета материалов при изменении глобального выбора;
  • Updates the global selection when the local selection is changed by sending a SelectionAction to the Undo stack.обновляет глобальный выбор, когда локальный выбор изменяется, отправляя SelectionAction в стек отмены;

Engine Gui Window Plugin
Плагин Engine Gui Window#

This plugin template implements a simple dockable window of the UnigineEditor with a simple GUI example that displays the current selection in the World Nodes hierarchy window, and enables you to assign a C++ component to selected nodes (see the picture below).Этот шаблон подключаемого модуля реализует простое закрепляемое окно UnigineEditor с простым примером графического интерфейса, который отображает текущий выбор в окне иерархии World Nodes и позволяет назначать компонент C++ выбранным узлам (см. рисунок ниже).

Engine Gui Window Plugin

Engine Gui Window PluginПлагин Engine Gui Window

В целом, архитектура плагина выглядит следующим образом:

The EngineGuiWindowPlugin class is an initialization point and complete representation of the plugin. Класс EngineGuiWindowPlugin — это точка инициализации и полное представление плагина.

The plugin interacts with Editor's external subsystems:Плагин взаимодействует с внешними подсистемами редактора:

  • Adds a widget to WindowManager.добавляет виджет в WindowManager;
  • Adds a new item to the main menu bar.добавляет новый пункт в строку главного меню;
  • Interacts with Editor's Drag&Drop System and enables dragging and dropping files within the window area, prints their names to the Console.взаимодействует с системой Drag&Drop редактора и позволяет перетаскивать файлы в области окна, выводит их имена в консоль;
  • Monitors current window focus state.отслеживает текущее состояние фокуса окна;
  • Updates the global selection when the local selection is changed by sending a SelectionAction to the Undo stack, when a node is selected and the Add components to selection! button is clicked.обновляет глобальный выбор при изменении локального выбора путем отправки SelectionAction в стек отмены при выборе узла и нажатии кнопки Add components to selection!.

And the following Engine subsystems:и следующими подсистемами Engine:

  • C++ Component Systemсистема компонентов С++;
  • GUI Systemсистема графического интерфейса;
  • Input Systemсистема ввода.

Using Plugin Template
Использование шаблона плагина#

To start developing your plugin for UnigineEditor perform the following actions:Чтобы начать разработку вашего плагина для UnigineEditor, выполните следующие действия:

  1. Check the Editor Plugin Template option when creating a new project in SDK Browser (or choose Other Actions -> Configure Project if you want to use your new Editor plugin in an existing project).Включите опцию Editor Plugin Template при создании нового проекта в SDK Browser (или выберите Other Actions -> Configure Project, если вы хотите использовать новый плагин Editor в существующем проекте).

    Adding a plugin template

  2. Click Create New Project (or Update Configuration if you're adding a template to an existing project).Нажмите Create New Project (или Update Configuration, если вы добавляете шаблон в существующий проект).

After clicking the Create New Project / Update Configuration button, a set of projects for the Editor plugins will be added to your UNIGINE project's source folder and all necessary files will be added to these folders depending on your operating system and API + IDE selection in project settings:После нажатия кнопки Create New Project / Update Configuration набор проектов для плагинов Editor будет добавлен в папку source вашего проекта UNIGINE, и в эти папки будут добавлены все необходимые файлы в зависимости от вашей операционной системы и выбора API + IDE в настройках проекта:

  • Windows OS: C++ (Qt-based / CMake) or UnigineScriptCMake will be used to build your plugin.Windows OS: C++ (Qt-based / CMake) или UnigineScriptCMake будет использоваться для сборки вашего плагина.
  • Windows OS: C# (.NET) or C++ (Visual Studio) — a .vcxproj project will be created and Visual Studio 2022 will be used to build your plugin.Windows OS: C# (.NET) или C++ (Visual Studio) — будет создан проект .vcxproj, а Visual Studio 2022 будет использоваться для сборки вашего плагина.
  • LinuxCMake will be used to build your plugin regardless of project configuration.LinuxCMake будет использоваться для сборки вашего плагина независимо от конфигурации проекта.

You can open this project in your IDE, build it, and give it a test drive right in the Editor as the project is pre-configured to launch with the UnigineEditor.Вы можете открыть этот проект в своей среде IDE, собрать его и протестировать прямо в редакторе, так как проект предварительно настроен для запуска с UnigineEditor.

Plugin's Logic Implementation
Реализация логики плагина#

Now let's have a quick look at the implementation of our plugin template's logic. As an example let's choose the Materials plugin.Теперь давайте кратко рассмотрим реализацию логики нашего шаблона плагина. В качестве примера выберем плагин Materials.

Plugin Class
Класс плагина#

The files MaterialsPlugin.h and MaterialsPlugin.cpp define the implementation of our plugin. Let's highlight some important points here.Файлы MaterialsPlugin.h и MaterialsPlugin.cpp определяют реализацию нашего плагина. Выделим здесь несколько важных моментов.

Header File
Заголовочный файл#

The header file MaterialsPlugin.h defines the interface of the plugin class.Заголовочный файл MaterialsPlugin.h определяет интерфейс класса плагина.

MaterialsPlugin.h
namespace Materials
{

The plugin is defined in a Materials namespace, which conforms to the coding rules for namespacing in sources.Плагин определен в пространстве имен Materials, которое соответствует правилам кодирования для пространства имен в источниках.

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

All Editor plugins must be derived from Editor::Plugin class and are QObjects. The Q_PLUGIN_METADATA macro is necessary to create a valid Editor plugin. The IID given in the macro must be UNIGINE_EDITOR_PLUGIN_IID, to identify it as an Editor plugin, and FILE must point to the plugin's meta data file as described in Plugin Meta Data.Все плагины редактора должны быть производными от класса Editor::Plugin и иметь класс QObjects. Макрос Q_PLUGIN_METADATA необходим для создания корректного плагина Editor. IID, указанный в макросе, должен быть UNIGINE_EDITOR_PLUGIN_IID, чтобы идентифицировать его как плагин редактора, а FILE должен указывать на файл метаданных плагина, как описано в разделе Метаданные плагина .

MaterialsPlugin.h
bool init() override;
void shutdown() override;

The base class defines basic functions that are called during the life cycle of a plugin (on initialization and shutdown), which are here implemented for your new plugin.Базовый класс определяет базовые функции, которые вызываются во время жизненного цикла плагина (при инициализации и выключении), которые здесь реализованы для вашего нового плагина.

Our plugin has three additional custom slots, that are used show the view, set global selection and update the local one when the global is changed.В нашем плагине есть три дополнительных настраиваемых слота, которые используются для отображения представления, установки глобального выбора и обновления локального выбора при изменении глобального.

MaterialsPlugin.h
public slots:
	void showView();
	void setGlobalSelection(const QModelIndexList &indexes);
	void globalSelectionChanged();

MaterialsPlugin.h

MaterialsPlugin.h
#pragma once

#include <editor/UniginePlugin.h>

#include <QObject>
#include <QModelIndex>

namespace Materials
{

class MaterialsModel;
class MaterialsView;
}

class QAction;

namespace Materials
{
class MaterialsPlugin : public QObject, public ::UnigineEditor::Plugin
{
	Q_OBJECT
	Q_DISABLE_COPY(MaterialsPlugin)
	Q_PLUGIN_METADATA(IID UNIGINE_EDITOR_PLUGIN_IID FILE "MaterialsPlugin.json")
	Q_INTERFACES(UnigineEditor::Plugin)
public:
	MaterialsPlugin() = default;
	~MaterialsPlugin() override;
	bool init() override;
	void shutdown() override;
public slots:
	void showView();
	void setGlobalSelection(const QModelIndexList &indexes);
	void globalSelectionChanged();
private:
	MaterialsModel *model_{nullptr};
	MaterialsView *view_{nullptr};
	QAction *action_{nullptr};
};

} // namespace Materials

Implementation File
Файл реализации#

This file contains the actual implementation of our plugin and its logic. For more information about implementing the plugin interface, see the Editor::Plugin API documentation and Plugin Life Cycle.Этот файл содержит фактическую реализацию нашего плагина и его логику. Дополнительные сведения о реализации интерфейса подключаемого модуля см. в документации по API Editor::Plugin и описании Жизненного цикла плагина .

MaterialsPlugin.cpp

MaterialsPlugin.h
#include "MaterialsPlugin.h"
#include "MaterialsView.h"
#include "MaterialsModel.h"
#include "QtMetaTypes.h"

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

#include <QObject>
#include <QMenu>

#include <algorithm>
#include <iterator>

using ::UnigineEditor::Selection;
using ::UnigineEditor::SelectionAction;
using ::UnigineEditor::WindowManager;
using ::UnigineEditor::SelectorGUIDs;

namespace Materials
{

MaterialsPlugin::~MaterialsPlugin() = default;

bool MaterialsPlugin::init()
{
	model_ = new MaterialsModel(this);

	view_ = new MaterialsView(model_);
	view_->setWindowTitle("Plugin - Material List");
	view_->setObjectName("PluginMaterialsView");
	WindowManager::add(view_, WindowManager::ROOT_AREA_LEFT);
	connect(view_, &MaterialsView::selected, this, &MaterialsPlugin::setGlobalSelection);

	QMenu *menu = WindowManager::findMenu(::UnigineEditor::Constants::MM_WINDOWS);
	action_ = menu->addAction("Plugin - Material List", this, &MaterialsPlugin::showView);

	connect(Selection::instance(), &Selection::changed,
			this, &MaterialsPlugin::globalSelectionChanged);

	return true;
}

void MaterialsPlugin::shutdown()
{
	QMenu *menu = WindowManager::findMenu(::UnigineEditor::Constants::MM_WINDOWS);
	menu->removeAction(action_);
	action_ = nullptr;

	disconnect(
		Selection::instance(), &Selection::changed, this, &MaterialsPlugin::globalSelectionChanged);

	disconnect(view_);
	WindowManager::remove(view_);

	delete view_;
	view_ = nullptr;

	delete model_;
	model_ = nullptr;
}

void MaterialsPlugin::showView()
{
	WindowManager::show(view_);
}

void MaterialsPlugin::setGlobalSelection(const QModelIndexList &indexes)
{
	disconnect(Selection::instance(), &Selection::changed,
			this, &MaterialsPlugin::globalSelectionChanged);

	Unigine::Vector<Unigine::UGUID> guids;
	guids.reserve(indexes.size());
	std::transform(std::begin(indexes), std::end(indexes), std::back_inserter(guids),
			[](const QModelIndex &idx)
			{ return idx.data(MaterialsModel::GUID_ROLE).value<Unigine::UGUID>(); });
	SelectionAction::applySelection(SelectorGUIDs::createMaterialsSelector(guids));

	connect(Selection::instance(), &Selection::changed,
			this, &MaterialsPlugin::globalSelectionChanged);
}

void MaterialsPlugin::globalSelectionChanged()
{
	using namespace std;

	if (auto selector = Selection::getSelectorMaterials())
	{
		const Unigine::Vector<Unigine::UGUID> &guids = selector->guids();
		QModelIndexList indexes;
		indexes.reserve(guids.size());
		transform(begin(guids), end(guids), back_inserter(indexes),
				[this](const Unigine::UGUID &guid) { return model_->index(guid); });

		auto it = remove(begin(indexes), end(indexes), QModelIndex());
		indexes.erase(it, end(indexes));

		QSignalBlocker blocker(view_);
		view_->globalSelectionChanged(indexes);
	}
	else if (auto selector = Selection::getSelectorRuntimes())
	{
		// implementation
	}
	else
	{
		view_->clearSelection();
	}
}

} // namespace Materials

Model Class
Класс Model#

Header File
Заголовочный файл#

MaterialsModel.h

MaterialsModel.h
#pragma once

#include <QAbstractItemModel>

#include <UnigineGUID.h>

namespace Materials
{

class MaterialsModel final : public QAbstractItemModel
{
	class Item;
	Q_DISABLE_COPY(MaterialsModel)
public:
	enum Role
	{
		GUID_ROLE = Qt::UserRole + 1
	};

	explicit MaterialsModel(QObject *parent = nullptr);
	~MaterialsModel() override;

	QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
	QModelIndex index(const Unigine::UGUID &guid) const;
	QModelIndex parent(const QModelIndex &child) const override;
	int rowCount(const QModelIndex &parent = QModelIndex()) const override;
	int columnCount(const QModelIndex &parent = QModelIndex()) const override;
	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

protected:
	void fetchMore(const QModelIndex &parent) override;
	bool canFetchMore(const QModelIndex &parent) const override;

private:
	Item *const root_;
};

} // namespace Materials

Implementation File
Файл реализации#

MaterialsModel.cpp

MaterialsModel.cpp
#include "MaterialsModel.h"
#include "QtMetaTypes.h"
#include <UnigineMaterials.h>
#include <UnigineFileSystem.h>

#include <QDebug>

using Unigine::UGUID;

namespace Materials
{

////////////////////////////////////////////////////////////////////////////////
// MaterialsModel::Item.
////////////////////////////////////////////////////////////////////////////////
class MaterialsModel::Item
{
public:
	static Item *createRootItem(MaterialsModel *model);

	Item(Item *parent, MaterialsModel *model, const UGUID &guid);
	~Item();

	QModelIndex index() const;

	void append(Item *item);
	Item *child(int idx);
	Item *find(const UGUID &guid) const;
	int childCount() const;

	Item *parent();
	int row() const;
	QVariant data(int role) const;

	void fetchMore();
	bool canFetchMore() const;

private:
	Item *parent_ = nullptr;			// not owned
	MaterialsModel *model_ = nullptr;	// not owned
	QVector<Item *> children_;			// owned
	UGUID guid_;
};

MaterialsModel::Item *MaterialsModel::Item::createRootItem(MaterialsModel *model)
{
	Item *root = new Item(nullptr, model, UGUID());
	const int size = Unigine::Materials::getNumMaterials();
	for (int i = 0; i < size; ++i)
	{
		const Unigine::MaterialPtr mat = Unigine::Materials::getMaterial(i);
		if (mat->isHidden())
		{
			continue;
		}
		if (const Unigine::MaterialPtr &parent_mat = mat->getParent())
		{
			if (!parent_mat->isHidden())
			{
				continue;
			}
		}
		root->children_.push_back(new Item(root, model, mat->getGUID()));
	}
	return root;
}

MaterialsModel::Item::Item(Item *parent, MaterialsModel *model, const UGUID &guid)
	: parent_(parent), model_(model), guid_(guid)
{
}

MaterialsModel::Item::~Item()
{
	qDeleteAll(children_);
}

QModelIndex MaterialsModel::Item::index() const
{
	if (!parent_ || guid_.isEmpty())
	{
		return QModelIndex();
	}
	return model_->createIndex(row(), 0, const_cast<Item *>(this));
}

void MaterialsModel::Item::append(Item *item)
{
	const int size = children_.size();
	model_->beginInsertRows(index(), size, size);
	item->parent_ = this;
	children_.push_back(item);
	model_->endInsertRows();
}

auto MaterialsModel::Item::child(int idx) -> Item *
{
	return ((idx >= 0) && (idx < children_.size()))
		? children_[idx]
		: nullptr;
}

auto MaterialsModel::Item::find(const UGUID &guid) const -> Item *
{
	for (Item *child: children_)
	{
		if (child->guid_ == guid)
		{
			return child;
		}
		if (child->canFetchMore())
		{
			child->fetchMore();
		}
		if (Item *grandchild = child->find(guid))
		{
			return grandchild;
		}
	}
	return nullptr;
}

int MaterialsModel::Item::childCount() const
{
	if (!guid_.isValid())
	{
		return children_.size();
	}

	const Unigine::MaterialPtr mat = Unigine::Materials::findMaterialByGUID(guid_);
	if (!mat)
	{
		// TODO: handle processes of adding new materials and removing old ones

		return 0;
	}

	int count = mat->getNumChildren();
	for (int i = count - 1; i >= 0; --i)
	{
		if (mat->getChild(i)->isHidden())
		{
			--count;
		}
	}
	return count;
}

MaterialsModel::Item *MaterialsModel::Item::parent()
{
	return parent_;
}

int MaterialsModel::Item::row() const
{
	if (parent_)
	{
		return parent_->children_.indexOf(const_cast<Item *>(this));
	}
	return 0;
}

QVariant MaterialsModel::Item::data(int role) const
{
	if (Qt::DisplayRole == role)
	{
		const Unigine::MaterialPtr &mat = Unigine::Materials::findMaterialByGUID(guid_);
		if (!mat)
		{
			// TODO: handle processes of adding new materials and removing old ones

			const QString name =
				tr("Removed - %1").arg(QLatin1String(guid_.makeString().get()));
			return QVariant::fromValue(name);
		}

		const UGUID &file_mat_guid = Unigine::FileSystem::getGUID(mat->getPath());
		const UGUID &asset_guid = Unigine::FileSystemAssets::resolveAsset(file_mat_guid);
		const Unigine::String &virtual_path = Unigine::FileSystem::getVirtualPath(asset_guid);
		const Unigine::StringStack<> file_name = Unigine::String::filename(virtual_path);
		return QVariant::fromValue(QString(file_name.get()));
	}
	if (MaterialsModel::GUID_ROLE == role)
	{
		return QVariant::fromValue(guid_);
	}
	return QVariant();
}

void MaterialsModel::Item::fetchMore()
{
	const Unigine::MaterialPtr &mat = Unigine::Materials::findMaterialByGUID(guid_);
	if (!mat)
	{
		// TODO: handle processes of adding new materials and removing old ones
		
		return;
	}

	const int size = mat->getNumChildren();
	QVector<Item *> items;
	items.reserve(size);

	for (int i = 0; i < size; ++i)
	{
		const Unigine::MaterialPtr child_mat = mat->getChild(i);
		if (child_mat->isHidden())
		{
			continue;
		}
		items.push_back(new Item(this, model_, child_mat->getGUID()));
	}

	if (items.empty())
	{
		return;
	}

	const int cur_size = children_.size();
	model_->beginInsertRows(index(), cur_size, cur_size + items.size() - 1);
	std::copy(std::begin(items), std::end(items), std::back_inserter(children_));
	model_->endInsertRows();
}

bool MaterialsModel::Item::canFetchMore() const
{
	return children_.empty() && childCount();
}

////////////////////////////////////////////////////////////////////////////////
// MaterialsModel.
////////////////////////////////////////////////////////////////////////////////
MaterialsModel::MaterialsModel(QObject *parent)
	: QAbstractItemModel(parent)
	, root_(Item::createRootItem(this))
{
}

MaterialsModel::~MaterialsModel()
{
	delete root_;
}

QModelIndex MaterialsModel::index(int row, int column, const QModelIndex &parent) const
{
	if (column > 0)
	{
		return {};
	}

	Item *parent_item = nullptr;
	if (!parent.isValid())
	{
		parent_item = root_;
	}
	else
	{
		parent_item = static_cast<Item *>(parent.internalPointer());
	}

	Item *child_item = parent_item->child(row);
	return child_item
		? createIndex(row, column, child_item)
		: QModelIndex();
}

QModelIndex MaterialsModel::index(const UGUID &guid) const
{
	if (guid.isEmpty())
	{
		return QModelIndex();
	}
	Item *item = root_->find(guid);
	return item ? item->index() : QModelIndex();
}

QModelIndex MaterialsModel::parent(const QModelIndex &child) const
{
	if (!child.isValid())
	{
		return QModelIndex();
	}
	Item *child_item = static_cast<Item *>(child.internalPointer());
	Item *parent_item = child_item->parent();

	return (parent_item != root_)
		? createIndex(parent_item->row(), 0, parent_item)
		: QModelIndex();
}

int MaterialsModel::rowCount(const QModelIndex &parent) const
{
	if (parent.column() > 0)
	{
		return 0;
	}

	Item *parent_item = nullptr;
	if (!parent.isValid())
	{
		parent_item = root_;
	}
	else
	{
		parent_item = static_cast<Item *>(parent.internalPointer());
	}

	return parent_item->childCount();
}

int MaterialsModel::columnCount(const QModelIndex &parent) const
{
	return 1;
}

QVariant MaterialsModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
	{
		return QVariant();
	}

	Item *item = static_cast<Item *>(index.internalPointer());
	return item->data(role);
}

void MaterialsModel::fetchMore(const QModelIndex &parent)
{
	if (!parent.isValid())
	{
		return;
	}
	Item *item = static_cast<Item *>(parent.internalPointer());
	item->fetchMore();
}

bool MaterialsModel::canFetchMore(const QModelIndex &parent) const
{
	if (!parent.isValid())
	{
		return false;
	}
	Item *item = static_cast<Item *>(parent.internalPointer());
	return item->canFetchMore();
}

} // namespace Materials

View Class
Класс View#

Header File
Заголовочный файл#

MaterialsView.h

MaterialsView.h
#pragma once

#include <QWidget>
#include <QModelIndex>

#include <UnigineGUID.h>

class QAbstractItemModel;
class QItemSelection;
class QTreeView;

namespace Materials
{

class MaterialsView : public QWidget
{
	Q_OBJECT
	Q_DISABLE_COPY(MaterialsView)
public:
	explicit MaterialsView(QAbstractItemModel *model, QWidget *parent = nullptr);
	~MaterialsView() override;

signals:
	void selected(const QModelIndexList &indexes);

public slots:
	void clearSelection();
	void globalSelectionChanged(const QModelIndexList &indexes);

private slots:
	void selectionChanged(const QItemSelection &selection);

private:
	QTreeView *view_ = nullptr;
};

} // namespace Materials

Implementation File
Файл реализации#

MaterialsView.cpp

MaterialsView.cpp
#include "MaterialsView.h"

#include <QAbstractItemModel>
#include <QVBoxLayout>
#include <QTreeView>

namespace Materials
{

MaterialsView::MaterialsView(QAbstractItemModel *model, QWidget *parent)
	: QWidget{parent}
{
	view_ = new QTreeView();
	view_->setAlternatingRowColors(true);
	view_->setSelectionMode(QAbstractItemView::ExtendedSelection);
	view_->setSelectionBehavior(QAbstractItemView::SelectRows);
	view_->setHeaderHidden(true);

	auto vl = new QVBoxLayout(this);
	vl->setContentsMargins(2, 2, 2, 2);
	vl->addWidget(view_);

	view_->setModel(model);

	connect(view_->selectionModel(), &QItemSelectionModel::selectionChanged
			, this, &MaterialsView::selectionChanged);
}

MaterialsView::~MaterialsView() = default;

void MaterialsView::clearSelection()
{
	view_->selectionModel()->clearSelection();
}

void MaterialsView::globalSelectionChanged(const QModelIndexList &indexes)
{
	if (indexes.empty())
	{
		clearSelection();
		return;
	}

	view_->setCurrentIndex(indexes.back());

	QItemSelection item_selection;
	for (const QModelIndex &index: indexes)
	{
		item_selection.append(QItemSelectionRange(index));
	}
	view_->selectionModel()->select(item_selection, QItemSelectionModel::ClearAndSelect);

	view_->scrollTo(indexes.back());
}

void MaterialsView::selectionChanged(const QItemSelection &selection)
{
	emit selected(view_->selectionModel()->selectedIndexes());
}

} // namespace Materials

Auxiliary File
Вспомогательный файл#

This file contains registration of additional metatypes to be used in our plugin. To make them known to Qt's meta-object system (to be able to put and get Unigine::UGUID to and out of the QVariant), we need Unigine::UGUID.Этот файл содержит регистрацию дополнительных метатипов, которые будут использоваться в нашем плагине. Чтобы сделать их известными системе метаобъектов Qt (чтобы иметь возможность помещать и получать Unigine::UGUID из QVariant), нам нужен Unigine::UGUID.

Примечание
This file should be saved to the source folder with all other source files.Этот файл следует сохранить в папку source со всеми остальными исходными файлами.

QtMetaTypes.h

QtMetaTypes.h
#pragma once

// including UnigineGUID.h containing definition of the metatype we're going to declare
#include <UnigineGUID.h>
// including QMetaType to be able to declare metatypes
#include <QMetaType>

// registering the Unigine::UGUID type to make it known to Qt's meta-object system
Q_DECLARE_METATYPE(Unigine::UGUID)

Building the Plugin
Сборка плагина#

Using MS Visual Studio
Использование MS Visual Studio#

In case you're using MS Visual Studio, there is a .vcxproj file in your plugin folder just select the Release configuration and choose Build Solution in the main menu. The project is pre-configured to launch with the UnigineEditor so you can run and test it right from your IDE (e.g. simply hit the default Ctrl + F5 hotkey in Visual Studio).Если вы используете MS Visual Studio, в папке вашего плагина есть файл .vcxproj, просто установить конфигурацию Release и выберите Build Solution в главном меню. Проект предварительно сконфигурирован для запуска с UnigineEditor, поэтому вы можете запустить и протестировать его прямо из вашей IDE (например, просто нажмите горячую клавишу Ctrl + F5 по умолчанию в Visual Studio).

Примечание
Release version of the Editor requires Release binaries of plugins and does not load Debug ones!Редактор, имеющий версию Release, работает только с Release-версиями плагинов и не загружает сборки с конфигурацией Debug!

Using CMake
Использование CMake#

All files required to build our plugin using CMake (CMakeList.txt and Materials.json describing our Materials plugin and its dependencies) are automatically added to the corressponding folder of your plugin inside your UNIGINE project's source folder. So, launch CMake to build the plugin.Все файлы, необходимые для сборки нашего плагина с помощью CMake (CMakeList.txt и Materials.json, описывающие наш плагин Materials и его зависимости), автоматически добавляются в соответствующую папку вашего плагина внутри папки source вашего проекта UNIGINE. Итак, запустите CMake, чтобы собрать плагин.

Running the Plugin
Запуск плагина#

As the plugin is built, all resulting files are automatically copied to the %project_name%/bin/plugins/Unigine/ folder, so that UnigineEditor Plugin System could load it automatically.По мере сборки плагина все полученные файлы автоматически копируются в папку %project_name%/bin/plugins/Unigine/, так чтобы Система плагинов UnigineEditor могла загрузить его автоматически.

And finally we launch UnigineEditor to check our plugin. If everything was done properly, you will see the MaterialsPlugin plugin in Editor Plugin Manager (Windows -> Editor Plugin Manager).И, наконец, мы запускаем UnigineEditor, чтобы проверить наш плагин. Если все было сделано правильно, вы должны увидеть плагин MaterialsPlugin в Editor Plugin Manager (Windows -> Editor Plugin Manager).

Plugins List

Примечание
You can unload and load your plugin again when necessary via the corresponding button in the HotReload column.Вы можете выгрузить и снова загрузить свой плагин, когда это необходимо, с помощью соответствующей кнопки в столбце HotReload.
Последнее обновление: 01.08.2024
Build: ()