This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор 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
Учебные материалы

Импорт моделей непосредственно в память

Внимание
Функционал, описанный в этой статье, недоступен в Community редакции SDK.
Чтобы использовать этот функционал вам необходимо получить лицензию на Engineering / Sim SDK.

Система импорта UNIGINE позволяет импортировать модели и сцены в различных внешних форматах (FBX, DAE, OBJ и т.д.). По умолчанию (т. е. при использовании DefaultProcessor) для каждой импортируемой сцены или модели на диске создается набор соответствующих файлов в форматах файлов UNIGINE (.mesh, .dds, .node, и т. д.).

Однако в некоторых случаях может потребоваться импортировать модели непосредственно в текущую сцену "на лету", не создавая никаких ненужных файлов на диске (например, загрузка моделей зданий для приложения "умный город" и т. д.). Более того, такой подход может ускорить весь процесс импорта за счет сокращения количества медленных операций ввода-вывода с диска.

Для этой цели необходимо создать пользовательский процессор импорта.

В этом примере показано, как:

  • создать свой собственный пользовательский процессор импорта для плагина FbxImporter;
  • использовать свой пользовательский процессор импорта, чтобы перенести сцену, сохраненную в файле FBX, в загруженную в данный момент сцену UNIGINE.
Примечание
Рекомендуем прочитать статью Система импорта и ознакомиться с базовыми функциональными классами импорта.

Прежде чем мы начнем, немного теории (ключевые моменты):

  • UNIGINE API предлагает набор классов для реализации импорта настраиваемой модели.
  • Вся метаинформация об импортированной вами сцене должна храниться в экземпляре класса ImportScene. В принципе, сцена может содержать меши (ImportMesh), источники света (ImportLight), камеры (ImportCamera) и другие элементы (смотрите полный список здесь).
  • Пользовательский процессор импорта должен быть унаследован от класса ImportProcessor.

Creating a Custom Import ProcessorСоздание пользовательского процессора импорта#

Итак, давайте начнем с процессора. Назовем его MemoryProcessor и реализуем создание объектов в текущем мире UNIGINE из содержимого FBX-файла, пропуская ненужное сохранение данных на диск. Итак, мы просто пройдемся по компонентам импортированной сцены FBX (камеры, источники света и меши), построим соответствующую иерархию нод для добавления в мир и создадим все необходимые материалы и текстуры. Приведенный ниже код дополнен комментариями, объясняющими основные аспекты процесса.

Direct Memory Import Processor

Исходный код (C++)
// including necessary libraries
#include <UnigineImport.h>
#include <UnigineMaterials.h>
#include <UnigineMeshStatic.h>

using namespace Unigine;
// our custom ImportProcessor to be used for direct memory import of scenes
class MemoryProcessor : public ImportProcessor
{
public:

	void convertNode(const NodePtr &node_parent, const ImportNodePtr &import_node_parent,
		NodePtr &node, const ImportNodePtr &import_node)
	{
		using namespace Unigine::Math;
		if (ImportCameraPtr import_camera = import_node->getCamera())
		{
			node = getImporter()->importCamera(getImportProcessor(), import_camera);
			if (node)
				node->setWorldTransform(Mat4(import_node->getTransform()));
		} else if (ImportLightPtr import_light = import_node->getLight())
		{
			node = getImporter()->importLight(getImportProcessor(), import_light);
			if (node)
				node->setWorldTransform(Mat4(import_node->getTransform()));
		} else if (ImportMeshPtr import_mesh = import_node->getMesh())
		{
			ObjectPtr object;
			if (import_mesh->isHasAnimations())
			{
				float fps = getImporter()->getParameterFloat("fps");
				auto mesh_skinned = ObjectMeshSkinned::create(meshes[import_mesh->getFilepath()]);
				if (MeshPtr animation = meshes_animations.value(import_mesh->getFilepath()))
				{
					int animation_id = mesh_skinned->addAnimation(animation);
					mesh_skinned->setNumLayers(1);
					mesh_skinned->setAnimation(0, animation_id);
				}
				mesh_skinned->setSpeed(fps);
				object = mesh_skinned;
			} else
			{
				auto mesh_static = ObjectMeshStatic::create();
				mesh_static->setMeshProceduralMode(true);
				mesh_static->applyMeshProcedural(meshes[import_mesh->getFilepath()]);

				object = mesh_static;
				object->setWorldTransform(Mat4(import_node->getTransform()));
			}

			int num_geometries = import_mesh->getNumGeometries();
			for (int i = 0; i < num_geometries; ++i)
			{
				ImportGeometryPtr geometry = import_mesh->getGeometry(i);
				int num_surfaces = geometry->getNumSurfaces();
				for (int s = 0; s < num_surfaces; ++s)
				{
					ImportSurfacePtr surface = geometry->getSurface(s);
					const int surface_index = surface->getTargetSurface();
					if (surface_index == -1 || surface_index >= object->getNumSurfaces())
					{
						Log::error("MemoryProcessor: can't find surface \"%s\".\n", surface->getName());
						continue;
					}

					if (!compare(surface->getMinVisibleDistance(), -Consts::INF))
					{
						object->setMinVisibleDistance(surface->getMinVisibleDistance(),
							surface_index);
					}
					if (!compare(surface->getMaxVisibleDistance(), Consts::INF))
					{
						object->setMaxVisibleDistance(surface->getMaxVisibleDistance(),
							surface_index);
					}

					object->setMinFadeDistance(surface->getMinFadeDistance(), surface_index);
					object->setMaxFadeDistance(surface->getMaxFadeDistance(), surface_index);

					if (ImportMaterialPtr import_material = surface->getMaterial())
					{
						object->setMaterial(materials[import_material->getFilepath()],
							surface_index);
					}
				}
			}

			node = object;
		} else
		{
			node = NodeDummy::create();
			node->setWorldTransform(Mat4(import_node->getTransform()));
		}

		node->setName(import_node->getName());

		getImporter()->importNodeChild(getImportProcessor(), node_parent, import_node_parent,
			node, import_node);

		int num_children = import_node->getNumChildren();
		for (int i = 0; i < num_children; ++i)
		{
			NodePtr child;
			convertNode(node, import_node, child, import_node->getChild(i));
			node->addWorldChild(child);
		}
	}

protected:
	// method to be called on mesh processing
	bool onProcessMesh(const MeshPtr &mesh, const ImportMeshPtr &import_mesh) override
	{
		UGUID guid = generate_unique_guid();
		import_mesh->setFilepath(guid.getString());
		meshes.append(import_mesh->getFilepath(), mesh);
		return true;
	}

	// method to be called on mesh animation processing
	bool onProcessAnimation(const MeshPtr &animation, const ImportMeshPtr &import_mesh,
		const ImportAnimationPtr &import_animation) override
	{
		meshes_animations.append(import_mesh->getFilepath(), animation);
		return true;
	}

	// method to be called on texture processing
	bool onProcessTexture(const ImportTexturePtr &import_texture) override
	{
		import_texture->setFilepath(import_texture->getOriginalFilepath());
		return true;
	}

	// method to be called on material processing
	bool onProcessMaterial(const MaterialPtr &material,
		const ImportMaterialPtr &import_material) override
	{
		UGUID guid = generate_unique_guid();
		import_material->setFilepath(guid.getString());
		materials.append(guid.getString(), material);
		return true;
	}

private:
	UGUID generate_unique_guid()
	{
		UGUID guid;
		guid.generate();

		int attempt = 100;
		while(guids.contains(guid) && attempt-- > 0)
			guid.generate();

		guids.append(guid);

		return guid;
	}

private:
	HashSet<UGUID> guids;
	// HashMaps to be used for imported meshes and materials
	HashMap<String, MeshPtr> meshes;
	HashMap<String, MeshPtr> meshes_animations;
	HashMap<String, MaterialPtr> materials;

};

Using Our Import ProcessorИспользование процессора импорта#

У нас есть наш пользовательский процессор импорта, давайте напишем функцию, использующую его для импорта содержимого из указанного FBX в загруженный в данный момент UNIGINE world.

Функция импорта

Исходный код (C++)
/// function importing the contents of the specified FBX to the currently loaded scene
NodePtr import(const char *filepath)
{
	// creating an importer for our fbx file
	ImporterPtr importer = Import::createImporterByFileName(filepath);
	if (!importer)
		return nullptr;

	if (!importer->init(filepath))
		return nullptr;

	// creating our custom MemoryProcessor and using it to import meshes, textures, materials, and nodes without saving data to files on disk
	MemoryProcessor memory_processor;
	ImportScenePtr scene = importer->getScene();
	int num_meshes = scene->getNumMeshes();
	for (int i = 0; i < num_meshes; ++i)
	{
		MeshPtr m = Mesh::create();
		importer->importMesh(memory_processor.getImportProcessor(), m, scene->getMesh(i));
		if (m->getNumBones())
		{
			int num_animations = scene->getNumAnimations();
			for (int i = 0; i < num_animations; ++i)
			{
				MeshPtr animation_mesh = Mesh::create();
				importer->importAnimation(memory_processor.getImportProcessor(), animation_mesh,
					scene->getMesh(i), scene->getAnimation(i));
			}
		}
	}

	int num_textures = scene->getNumTextures();
	for (int i = 0; i < num_textures; ++i)
	{
		importer->importTexture(memory_processor.getImportProcessor(), scene->getTexture(i));
	}
	
	MaterialPtr mesh_base = Materials::findManualMaterial("Unigine::mesh_base");
	int num_materials = scene->getNumMaterials();
	for (int i = 0; i < num_materials; ++i)
	{
		MaterialPtr m = mesh_base->inherit();
		importer->importMaterial(memory_processor.getImportProcessor(), m, scene->getMaterial(i));
	}

	int num_nodes = scene->getNumNodes();
	ImportNodePtr root_node;
	for (int i = 0; i < num_nodes; ++i)
	{
		const ImportNodePtr &node = scene->getNode(i);
		if (node->getParent() == nullptr)
		{
			root_node = node;
			break;
		}
	}

	if (root_node)
	{
		NodePtr node;
		memory_processor.convertNode(nullptr, nullptr, node, root_node);
		return node;
	}

	return nullptr;
}

Теперь мы можем просто загрузить плагин FbxImporter и использовать нашу функцию import() для добавления желаемой модели.

Исходный код (C++)
// including necessary libraries
#include <UnigineConsole.h>

// ...

// loading the FbxImporter plugin
Console::run("plugin_load FbxImporter");
Console::flush();
	
// importing an FBX-model directly to the scene
NodePtr imported_node = import("material_ball.fbx");

Example CodeПример кода#

Чтобы протестировать наш пользовательский процессор импорта, можно создать новый проект C++ в SDK Browser и добавить весь приведенный ниже код в файлы AppWorldLogic.h и AppWorldLogic.cpp.

AppWorldLogic.h

Исходный код (C++)
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__

#include <UnigineLogic.h>
#include <UnigineStreams.h>

class AppWorldLogic : public Unigine::WorldLogic
{

public:
	AppWorldLogic();
	virtual ~AppWorldLogic();

	int init() override;

	int update() override;
	int postUpdate() override;
	int updatePhysics() override;

	int shutdown() override;

	int save(const Unigine::StreamPtr &stream) override;
	int restore(const Unigine::StreamPtr &stream) override;

private:
	void run_animation(const Unigine::NodePtr &node);
	void setup_gui();
	void setup_player(const Unigine::NodePtr &node);
	void import_file(const Unigine::String &filepath);
	void dialog_file_ok_clicked(Unigine::WidgetDialogFilePtr dialog);
	void dialog_file_cancel_clicked(Unigine::WidgetDialogFilePtr dialog);
	void request_import(const Unigine::WidgetPtr &sender);

	Unigine::NodePtr node_;
	Unigine::String default_dialog_path{"./"};
};

#endif // __APP_WORLD_LOGIC_H__

AppWorldLogic.cpp

Исходный код (C++)
#include "AppWorldLogic.h"
#include <UnigineMathLib.h>
// including necessary libraries
#include <UnigineImport.h>
#include <UnigineMaterials.h>
#include <UnigineMeshStatic.h>
#include <UnigineGame.h>
#include <UnigineWindowManager.h>
using namespace Unigine;
// our custom ImportProcessor to be used for direct memory import of scenes
class MemoryProcessor : public ImportProcessor
{
public:

	void convertNode(const NodePtr &node_parent, const ImportNodePtr &import_node_parent,
		NodePtr &node, const ImportNodePtr &import_node)
	{
		using namespace Unigine::Math;
		if (ImportCameraPtr import_camera = import_node->getCamera())
		{
			node = getImporter()->importCamera(getImportProcessor(), import_camera);
			if (node)
				node->setWorldTransform(Mat4(import_node->getTransform()));
		} else if (ImportLightPtr import_light = import_node->getLight())
		{
			node = getImporter()->importLight(getImportProcessor(), import_light);
			if (node)
				node->setWorldTransform(Mat4(import_node->getTransform()));
		} else if (ImportMeshPtr import_mesh = import_node->getMesh())
		{
			ObjectPtr object;
			if (import_mesh->isHasAnimations())
			{
				float fps = getImporter()->getParameterFloat("fps");
				auto mesh_skinned = ObjectMeshSkinned::create(meshes[import_mesh->getFilepath()]);
				if (MeshPtr animation = meshes_animations.value(import_mesh->getFilepath()))
				{
					int animation_id = mesh_skinned->addAnimation(animation);
					mesh_skinned->setNumLayers(1);
					mesh_skinned->setAnimation(0, animation_id);
				}
				mesh_skinned->setSpeed(fps);
				object = mesh_skinned;
			} else
			{
				auto mesh_static = ObjectMeshStatic::create();
				mesh_static->setMeshProceduralMode(true);
				mesh_static->applyMeshProcedural(meshes[import_mesh->getFilepath()]);

				object = mesh_static;
				object->setWorldTransform(Mat4(import_node->getTransform()));
			}

			int num_geometries = import_mesh->getNumGeometries();
			for (int i = 0; i < num_geometries; ++i)
			{
				ImportGeometryPtr geometry = import_mesh->getGeometry(i);
				int num_surfaces = geometry->getNumSurfaces();
				for (int s = 0; s < num_surfaces; ++s)
				{
					ImportSurfacePtr surface = geometry->getSurface(s);
					const int surface_index = surface->getTargetSurface();
					if (surface_index == -1 || surface_index >= object->getNumSurfaces())
					{
						Log::error("MemoryProcessor: can't find surface \"%s\".\n", surface->getName());
						continue;
					}

					if (!compare(surface->getMinVisibleDistance(), -Consts::INF))
					{
						object->setMinVisibleDistance(surface->getMinVisibleDistance(),
							surface_index);
					}
					if (!compare(surface->getMaxVisibleDistance(), Consts::INF))
					{
						object->setMaxVisibleDistance(surface->getMaxVisibleDistance(),
							surface_index);
					}

					object->setMinFadeDistance(surface->getMinFadeDistance(), surface_index);
					object->setMaxFadeDistance(surface->getMaxFadeDistance(), surface_index);

					if (ImportMaterialPtr import_material = surface->getMaterial())
					{
						object->setMaterial(materials[import_material->getFilepath()],
							surface_index);
					}
				}
			}

			node = object;
		} else
		{
			node = NodeDummy::create();
			node->setWorldTransform(Mat4(import_node->getTransform()));
		}

		node->setName(import_node->getName());

		getImporter()->importNodeChild(getImportProcessor(), node_parent, import_node_parent,
			node, import_node);

		int num_children = import_node->getNumChildren();
		for (int i = 0; i < num_children; ++i)
		{
			NodePtr child;
			convertNode(node, import_node, child, import_node->getChild(i));
			node->addWorldChild(child);
		}
	}

protected:
	// method to be called on mesh processing
	bool onProcessMesh(const MeshPtr &mesh, const ImportMeshPtr &import_mesh) override
	{
		UGUID guid = generate_unique_guid();
		import_mesh->setFilepath(guid.getString());
		meshes.append(import_mesh->getFilepath(), mesh);
		return true;
	}

	// method to be called on mesh animation processing
	bool onProcessAnimation(const MeshPtr &animation, const ImportMeshPtr &import_mesh,
		const ImportAnimationPtr &import_animation) override
	{
		meshes_animations.append(import_mesh->getFilepath(), animation);
		return true;
	}

	// method to be called on texture processing
	bool onProcessTexture(const ImportTexturePtr &import_texture) override
	{
		import_texture->setFilepath(import_texture->getOriginalFilepath());
		return true;
	}

	// method to be called on material processing
	bool onProcessMaterial(const MaterialPtr &material,
		const ImportMaterialPtr &import_material) override
	{
		UGUID guid = generate_unique_guid();
		import_material->setFilepath(guid.getString());
		materials.append(guid.getString(), material);
		return true;
	}

private:
	UGUID generate_unique_guid()
	{
		UGUID guid;
		guid.generate();

		int attempt = 100;
		while(guids.contains(guid) && attempt-- > 0)
			guid.generate();

		guids.append(guid);

		return guid;
	}

private:
	HashSet<UGUID> guids;
	// HashMaps to be used for imported meshes and materials
	HashMap<String, MeshPtr> meshes;
	HashMap<String, MeshPtr> meshes_animations;
	HashMap<String, MaterialPtr> materials;

};
/// function importing the contents of the specified FBX to the currently loaded scene
NodePtr import(const char *filepath)
{
	// creating an importer for our fbx file
	ImporterPtr importer = Import::createImporterByFileName(filepath);
	if (!importer)
		return nullptr;

	if (!importer->init(filepath))
		return nullptr;

	// creating our custom MemoryProcessor and using it to import meshes, textures, materials, and nodes without saving data to files on disk
	MemoryProcessor memory_processor;
	ImportScenePtr scene = importer->getScene();
	int num_meshes = scene->getNumMeshes();
	for (int i = 0; i < num_meshes; ++i)
	{
		MeshPtr m = Mesh::create();
		importer->importMesh(memory_processor.getImportProcessor(), m, scene->getMesh(i));
		if (m->getNumBones())
		{
			int num_animations = scene->getNumAnimations();
			for (int i = 0; i < num_animations; ++i)
			{
				MeshPtr animation_mesh = Mesh::create();
				importer->importAnimation(memory_processor.getImportProcessor(), animation_mesh,
					scene->getMesh(i), scene->getAnimation(i));
			}
		}
	}

	int num_textures = scene->getNumTextures();
	for (int i = 0; i < num_textures; ++i)
	{
		importer->importTexture(memory_processor.getImportProcessor(), scene->getTexture(i));
	}
	
	MaterialPtr mesh_base = Materials::findManualMaterial("Unigine::mesh_base");
	int num_materials = scene->getNumMaterials();
	for (int i = 0; i < num_materials; ++i)
	{
		MaterialPtr m = mesh_base->inherit();
		importer->importMaterial(memory_processor.getImportProcessor(), m, scene->getMaterial(i));
	}

	int num_nodes = scene->getNumNodes();
	ImportNodePtr root_node;
	for (int i = 0; i < num_nodes; ++i)
	{
		const ImportNodePtr &node = scene->getNode(i);
		if (node->getParent() == nullptr)
		{
			root_node = node;
			break;
		}
	}

	if (root_node)
	{
		NodePtr node;
		memory_processor.convertNode(nullptr, nullptr, node, root_node);
		return node;
	}

	return nullptr;
}

AppWorldLogic::AppWorldLogic(){}

AppWorldLogic::~AppWorldLogic(){}

int AppWorldLogic::init()
{
	setup_gui();
	return 1;
}

void AppWorldLogic::run_animation(const NodePtr &node)
{
	if (ObjectMeshSkinnedPtr skinned = checked_ptr_cast<ObjectMeshSkinned>(node))
	{
		skinned->setLoop(1);
		skinned->play();
	}
	
	int num_children = node->getNumChildren();
	for (int i = 0; i < num_children; ++i)
		run_animation(node->getChild(i));
}

void AppWorldLogic::setup_gui()
{
	auto gui = Gui::getCurrent();

	auto window = WidgetWindow::create(gui, "Import File", 4, 4);

	auto import_button = WidgetButton::create(gui, "Import");
	import_button->getEventClicked().connect(this, &AppWorldLogic::request_import);

	window->addChild(import_button, Gui::ALIGN_EXPAND);
	window->arrange();

	gui->addChild(window, Gui::ALIGN_OVERLAP | Gui::ALIGN_LEFT | Gui::ALIGN_TOP);
}

void AppWorldLogic::setup_player(const NodePtr &node)
{
	static const float CAMERA_PHI = 75.0f;
	static const float CAMERA_THETA = 140.0f;
	static const float CAMERA_DISTANCE = 2.0f;
	static const float FOV = 60.0f;

	using namespace Unigine::Math;

	ivec2 window_size = WindowManager::getMainWindow()->getClientSize();

	WorldBoundSphere bound_sphere = node->getHierarchyBoundSphere();

	Vec3 center = bound_sphere.center;
	Scalar radius = bound_sphere.radius * CAMERA_DISTANCE *
		max(1.0f, float(window_size.y) / window_size.y);
	quat rotation= quat(1.0f, 0.0f, 0.0f, -CAMERA_PHI) * quat(0.0f, 0.0f, 1.0f, CAMERA_THETA);

	Mat4 modelview{translate(Scalar(0.0f), Scalar(0.0f), -radius) *
		Mat4(rotation) * translate(-center)};

	mat4 projection = perspective(FOV, 1.0f, radius * 0.01f, radius * 2.0f);
	if (PlayerPtr player = Game::getPlayer())
	{
		player->setWorldTransform(inverse(modelview));
		player->setProjection(projection);
	} else
		Log::error("Main player not found.\n");
}

void AppWorldLogic::import_file(const String &filepath)
{
	if (node_)
	{
		node_.deleteLater();
		node_ = nullptr;
	}

	node_ = import(filepath);
	if (node_)
	{
		Log::message("Node loaded \"%s\" from filepath \"%s\".\n",
			node_->getName(), filepath.get());
		setup_player(node_);
		run_animation(node_);
	}
	else
		Log::error("Can't import file from filepath %s\n", filepath.get());
}

void AppWorldLogic::dialog_file_ok_clicked(WidgetDialogFilePtr dialog)
{
	const String filepath = dialog->getFile();
	default_dialog_path = filepath.pathname();

	import_file(filepath);

	dialog.deleteLater();
}

void AppWorldLogic::dialog_file_cancel_clicked(WidgetDialogFilePtr dialog)
{
	dialog.deleteLater();
}

void AppWorldLogic::request_import(const WidgetPtr &sender)
{
	const Vector<String> &supported_extensions = Import::getSupportedExtensions();
	const String extensions_filter = "." + String::join(supported_extensions, ".");

	auto dialog_file = WidgetDialogFile::create(Gui::getCurrent(), "DialogFile");
	dialog_file->setPath(default_dialog_path);
	dialog_file->setFilter(extensions_filter);

	dialog_file->getOkButton()->getEventClicked().connect(this, &AppWorldLogic::dialog_file_ok_clicked, dialog_file);
	dialog_file->getCancelButton()->getEventClicked().connect(this, &AppWorldLogic::dialog_file_cancel_clicked, dialog_file);

	Gui::getCurrent()->addChild(dialog_file, Gui::ALIGN_OVERLAP | Gui::ALIGN_CENTER);

	dialog_file->setPermanentFocus();
}

////////////////////////////////////////////////////////////////////////////////
// start of the main loop
////////////////////////////////////////////////////////////////////////////////

int AppWorldLogic::update()
{
	// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.
	return 1;
}

int AppWorldLogic::postUpdate()
{
	// The engine calls this function after updating each render frame: correct behavior after the state of the node has been updated.
	return 1;
}

int AppWorldLogic::updatePhysics()
{
	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
	// The engine calls updatePhysics() with the fixed rate (60 times per second by default) regardless of the FPS value.
	// WARNING: do not create, delete or change transformations of nodes here, because rendering is already in progress.
	return 1;
}

////////////////////////////////////////////////////////////////////////////////
// end of the main loop
////////////////////////////////////////////////////////////////////////////////

int AppWorldLogic::shutdown()
{
	// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
	return 1;
}

int AppWorldLogic::save(const Unigine::StreamPtr &stream)
{
	// Write here code to be called when the world is saving its state (i.e. state_save is called): save custom user data to a file.
	UNIGINE_UNUSED(stream);
	return 1;
}

int AppWorldLogic::restore(const Unigine::StreamPtr &stream)
{
	// Write here code to be called when the world is restoring its state (i.e. state_restore is called): restore custom user data to a file here.
	UNIGINE_UNUSED(stream);
	return 1;
}
Последнее обновление: 27.02.2024
Build: ()