This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
UnigineEditor
界面概述
资产工作流程
设置和首选项
项目开发
调整节点参数
Setting Up Materials
Setting Up Properties
照明
Landscape Tool
Sandworm
使用编辑器工具执行特定任务
Extending Editor Functionality
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Objects
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
UnigineScript
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
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
创建内容
Content Optimization
Materials
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 nodeto 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->getNode();
			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->getNode();
			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;
					if (object->getMaterialName(surface_index) != String("mesh_base"))
						continue;
					object->setMaterial(materials[surface.material->name], surface_index);
				}
			}

			object->release();
			node = object->getNode();
		}
		else
		{
			// or importing a dummy node
			NodeDummyPtr dummy = NodeDummy::create();
			dummy->release();
			node = dummy->getNode();
			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++)
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::findBaseMaterial("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
Editor::addNode(import("test.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>

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 nodeto 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->getNode();
			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->getNode();
			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;
					if (object->getMaterialName(surface_index) != String("mesh_base"))
						continue;
					object->setMaterial(materials[surface.material->name], surface_index);
				}
			}

			object->release();
			node = object->getNode();
		}
		else
		{
			// or importing a dummy node
			NodeDummyPtr dummy = NodeDummy::create();
			dummy->release();
			node = dummy->getNode();
			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::findBaseMaterial("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() {
	// Write here code to be called on engine initialization.
	
	// loading the FbxImporter plugin
	Console::run("plugin_load FbxImporter");
	Console::flush();
	
	// importing an FBX-model directly to the scene
	Editor::addNode(import("test.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::render() {
	// Write here code to be called before rendering each render frame.
	
	return 1;
}
// end of the main loop

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

int AppSystemLogic::destroy() {
	// Write here code to be called when the video mode is changed or the application is restarted (i.e. video_restart is called). It is used to reinitialize the graphics context.
	
	return 1;
}
Last update: 2021-04-29
Build: ()