This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
专业(SIM)
UnigineEditor
界面概述
资产工作流程
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Landscape Tool
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Materials and Shaders
Rebuilding the Engine Tools
GUI
双精度坐标
应用程序接口
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
创建内容
内容优化
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
注意! 这个版本的文档是过时的,因为它描述了一个较老的SDK版本!请切换到最新SDK版本的文档。
注意! 这个版本的文档描述了一个不再受支持的旧SDK版本!请升级到最新的SDK版本。

Importing Models Directly to Memory

Warning
The functionality described in this article is not available in the Community SDK edition.
You should upgrade to Engineering / Sim SDK edition to use it.

UNIGINE's Import System allows you to import models and scenes in various external formats (FBX, DAE, OBJ, etc.). By default (i.e., when the DefaultProcessor is used) for each imported scene or model a set of corresponding files in UNIGINE's file formats (.mesh, .dds, .node, etc.) is created on disk.

However, in some cases it might be necessary to import models directly into your current scene on-the-fly, without creating any unnecessary files on disk (e.g. loading models of buildings for a smart city application, etc.). Moreover, this approach can speed up the whole importing process due to reduced number of slow disk I/O operations.

For this purpose we should create a custom import processor.

This example demonstrates how to:

  • Create your own custom import processor for the FbxImporter plugin.
  • Use your custom import processor to bring a scene stored in an FBX file to the currently loaded UNIGINE scene.
Notice
It is recommended that you've read the Import System article and familiarized with basic import functionality classes.

Before we get started, a little bit of theory (as key points).

  • UNIGINE's API offers us a set of classes for implementing customized model import.
  • All meta information about your imported scene should be stored in an instance of the ImportScene class. Basically a scene may contain meshes (ImportMesh), lights (ImportLight), cameras (ImportCamera), and other elements (see the complete list here).
  • A custom import processor should be inherited from the ImportProcessor class.

Creating a Custom Import Processor#

So, let's start with the processor. We call it MemoryProcessor and implement creation of objects in the current UNIGINE world from the contents of the FBX-file skipping unnecessary saving of data to disk. So we shall simply run through the components of the imported FBX scene (cameras, lights and meshes), build the corresponding node hierarchy to be added to the world and create all necessary materials and textures. The code below is supplemented with comments explaining the basic aspects of the process.

Direct Memory Import Processor

Source code (C++)
// including necessary libraries
#include <UnigineImport.h>
#include <UnigineHashMap.h>
#include <UnigineMaterials.h>

using namespace Unigine;

// our custom ImportProcessor to be used for direct memory import of FBX-scenes
class MemoryProcessor : public ImportProcessor
{
protected:
	// method to be called on mesh processing
	virtual bool onProcessMesh(MeshPtr &mesh, ImportMesh *import_mesh) override
	{
		UGUID guid;
		guid.generate();
		import_mesh->filepath = guid.getString();
		meshes.append(import_mesh->filepath, mesh);
		return true;
	}

	// method to be called on mesh animation processing
	bool onProcessAnimation(MeshPtr &animation, ImportMesh *import_mesh, ImportAnimation *import_animation)
	{
		return true;
	}

	// method to be called on texture processing
	bool onProcessTexture(ImportTexture *import_texture)
	{
		import_texture->filepath = import_texture->original_filepath;
		return true;
	}

	// method to be called on material processing
	bool onProcessMaterial(MaterialPtr &material, ImportMaterial *import_material)
	{
		UGUID guid;
		guid.generate();
		materials.append(import_material->name, material);
		return true;
	}

	// method performing node to be called on mesh processing
	void convert_node(NodePtr &node, ImportNode *import_node)
	{
		using namespace Unigine::Math;

		// checking node's type and performing the corresponding import operations
		if (import_node->camera != nullptr)
		{
			// importing a camera
			PlayerPtr player;
			getImporter()->importCamera(this, player, import_node->camera);
			node = player;
			node->setWorldTransform(Mat4(import_node->transform));
		}
		else if (import_node->light != nullptr)
		{
			// importing a camera
			LightPtr light;
			getImporter()->importLight(this, light, import_node->light);
			node = light;
			node->setWorldTransform(Mat4(import_node->transform));
		}
		else if (import_node->mesh != nullptr)
		{
			// importing a mesh
			ImportMesh *import_mesh = import_node->mesh;
			ObjectMeshStaticPtr object = ObjectMeshStatic::create(meshes[import_mesh->filepath]);
			object->setWorldTransform(Mat4(import_node->transform));

			for (const ImportGeometry &geometry : import_mesh->geometries)
			{
				for (const ImportSurface &surface : geometry.surfaces)
				{
					int surface_index = surface.target_surface;
					if (surface_index == -1)
					{
						Log::error("Can't find surface \"%s\".\n", surface.name.get());
						continue;
					}
					if (surface.material == nullptr)
						continue;

					object->setMaterial(materials[surface.material->name], surface_index);
				}
			}

			node = object;
		}
		else
		{
			// or importing a dummy node
			NodeDummyPtr dummy = NodeDummy::create();
			node = dummy;
			node->setWorldTransform(Mat4(import_node->transform));
		}

		node->setName(import_node->name);

		// recursively processing all node's children and adding them to the hierarchy in the world
		for (ImportNode *import_child : import_node->children)
		{
			NodePtr child;
			convert_node(child, import_child);
			node->addWorldChild(child);
		}
	}

	// method to be called on node processing
	virtual bool onProcessNode(NodePtr &node, ImportNode *import_node)
	{
		convert_node(node, import_node);
		return true;
	}

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

};

Using Our Import Processor#

We have got our custom import processor, let's write a function using it to import the contents of the specified FBX to the currently loaded UNIGINE world.

Import Function

Source code (C++)
/// function importing the contents of the specified FBX to the currently loaded scene
NodePtr import(const char *path)
{
	// creating an importer for our fbx file
	Importer *importer = Import::get()->createImporterByFileName(path);
	if (!importer)
		return NodePtr();

	if (!importer->init(path))
	{
		delete importer;
		return NodePtr();
	}
	// creating our custom MemoryProcessor and using it to import meshes, textures, materials, and nodes without saving data to files on disk
	MemoryProcessor memory_processor;
	ImportScene *scene = importer->getScene();
	for (ImportMesh *mesh : scene->getMeshes())
	{
		MeshPtr m = Mesh::create();
		importer->importMesh(&memory_processor, m, mesh);
	}

	for (ImportTexture *texture : scene->getTextures())
	{
		importer->importTexture(&memory_processor, texture);
	}
	
	MaterialPtr mesh_base = Materials::findManualMaterial("Unigine::mesh_base");
	for (ImportMaterial *material : scene->getMaterials())
	{
		MaterialPtr m = mesh_base->inherit();
		importer->importMaterial(&memory_processor, m, material);
	}

	for (ImportNode *node : scene->getNodes())
	{
		if (node->parent == nullptr)
		{
			NodePtr n;
			importer->importNode(&memory_processor, n, node);
			delete importer;
			return n;
		}
	}

	delete importer;
	return NodePtr();
}

Now we can just load the FbxImporter plugin and use our import() function to add the desired model.

Source code (C++)
// including necessary libraries
#include <UnigineConsole.h>
#include <UnigineEditor.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#

To test our custom import processor we can create a new C++ project in the SDK Browser and put all the code listed below to the AppSystemLogic.cpp

AppSystemLogic.cpp

Source code (C++)
// AppSystemLogic.cpp

#include "AppSystemLogic.h"

// including necessary libraries
#include <UnigineConsole.h>
#include <UnigineImport.h>
#include <UnigineHashMap.h>
#include <UnigineEditor.h>
#include <UnigineMaterials.h>
#include <UnigineMathLib.h>

using namespace Unigine;

// our custom ImportProcessor to be used for direct memory import of FBX-scenes
class MemoryProcessor : public ImportProcessor
{
protected:
	// method to be called on mesh processing
	virtual bool onProcessMesh(MeshPtr &mesh, ImportMesh *import_mesh) override
	{
		UGUID guid;
		guid.generate();
		import_mesh->filepath = guid.getString();
		meshes.append(import_mesh->filepath, mesh);
		return true;
	}

	// method to be called on mesh animation processing
	bool onProcessAnimation(MeshPtr &animation, ImportMesh *import_mesh, ImportAnimation *import_animation)
	{
		return true;
	}

	// method to be called on texture processing
	bool onProcessTexture(ImportTexture *import_texture)
	{
		import_texture->filepath = import_texture->original_filepath;
		return true;
	}

	// method to be called on material processing
	bool onProcessMaterial(MaterialPtr &material, ImportMaterial *import_material)
	{
		UGUID guid;
		guid.generate();
		materials.append(import_material->name, material);
		return true;
	}

	// method performing node to be called on mesh processing
	void convert_node(NodePtr &node, ImportNode *import_node)
	{
		using namespace Unigine::Math;

		// checking node's type and performing the corresponding import operations
		if (import_node->camera != nullptr)
		{
			// importing a camera
			PlayerPtr player;
			getImporter()->importCamera(this, player, import_node->camera);
			node = player;
			node->setWorldTransform(Mat4(import_node->transform));
		}
		else if (import_node->light != nullptr)
		{
			// importing a camera
			LightPtr light;
			getImporter()->importLight(this, light, import_node->light);
			node = light;
			node->setWorldTransform(Mat4(import_node->transform));
		}
		else if (import_node->mesh != nullptr)
		{
			// importing a mesh
			ImportMesh *import_mesh = import_node->mesh;
			ObjectMeshStaticPtr object = ObjectMeshStatic::create(meshes[import_mesh->filepath]);
			object->setWorldTransform(Mat4(import_node->transform));

			for (const ImportGeometry &geometry : import_mesh->geometries)
			{
				for (const ImportSurface &surface : geometry.surfaces)
				{
					int surface_index = surface.target_surface;
					if (surface_index == -1)
					{
						Log::error("Can't find surface \"%s\".\n", surface.name.get());
						continue;
					}
					if (surface.material == nullptr)
						continue;

					object->setMaterial(materials[surface.material->name], surface_index);
				}
			}

			node = object;
		}
		else
		{
			// or importing a dummy node
			NodeDummyPtr dummy = NodeDummy::create();
			node = dummy;
			node->setWorldTransform(Mat4(import_node->transform));
		}

		node->setName(import_node->name);

		// recursively processing all node's children and adding them to the hierarchy in the world
		for (ImportNode *import_child : import_node->children)
		{
			NodePtr child;
			convert_node(child, import_child);
			node->addWorldChild(child);
		}
	}

	// method to be called on node processing
	virtual bool onProcessNode(NodePtr &node, ImportNode *import_node)
	{
		convert_node(node, import_node);
		return true;
	}

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

};

/// function importing the contents of the specified FBX to the currently loaded scene
NodePtr import(const char *path)
{
	// creating an importer for our fbx file
	Importer *importer = Import::get()->createImporterByFileName(path);
	if (!importer)
		return NodePtr();

	if (!importer->init(path))
	{
		delete importer;
		return NodePtr();
	}
	// creating our custom MemoryProcessor and using it to import meshes, textures, materials, and nodes without saving data to files on disk
	MemoryProcessor memory_processor;
	ImportScene *scene = importer->getScene();
	for (ImportMesh *mesh : scene->getMeshes())
	{
		MeshPtr m = Mesh::create();
		importer->importMesh(&memory_processor, m, mesh);
	}

	for (ImportTexture *texture : scene->getTextures())
	{
		importer->importTexture(&memory_processor, texture);
	}

	MaterialPtr mesh_base = Materials::findManualMaterial("Unigine::mesh_base");
	for (ImportMaterial *material : scene->getMaterials())
	{
		MaterialPtr m = mesh_base->inherit();
		importer->importMaterial(&memory_processor, m, material);
	}

	for (ImportNode *node : scene->getNodes())
	{
		if (node->parent == nullptr)
		{
			NodePtr n;
			importer->importNode(&memory_processor, n, node);
			delete importer;
			return n;
		}
	}

	delete importer;
	return NodePtr();
}

AppSystemLogic::AppSystemLogic()
{
}

AppSystemLogic::~AppSystemLogic()
{
}

int AppSystemLogic::init() {
	// loading the FbxImporter plugin
	Console::run("plugin_load FbxImporter");
	Console::flush();

	NodePtr imported_node = import("material_ball.fbx");

	return 1;
}

// start of the main loop
int AppSystemLogic::update() {
	// Write here code to be called before updating each render frame.

	return 1;
}

int AppSystemLogic::postUpdate() {

	return 1;
}
// end of the main loop

int AppSystemLogic::shutdown() {
	// Write here code to be called on engine shutdown.

	return 1;
}
Last update: 2022-03-10
Build: ()