Programming
Fundamentals
Setting Up Development Environment
Usage Examples
UnigineScript
High-Level Systems
C++
C#
UUSL (Unified UNIGINE Shader Language)
File Formats
Rebuilding the Engine and Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
Rendering-Related Classes

9. Creating, Applying and Deleting Materials at Runtime

<< RETURN TO THE PREVIOUS SECTION

Materials assigned to particular surfaces of a node determine how the node is to be rendered. They implement the shaders and control what options, states, parameters of different types and textures are used to render the node during the rendering passes. To manage materials we use the following two classes:

  • Materials class which represents an interface for managing the list of material libraries that belong to the world.
  • Material class which is used to manage each individual material.
Notice
In order to use materials we must include the UnigineMaterials.h library
The following code can be used to create a new material library and add a new material inherited from the mesh_base material.
Source code (C++)
#include <UnigineMaterials.h>
#include <UnigineObjects.h>
using namespace Unigine;

/* .. */

int AppWorldLogic::init() 
{

	// creating a box (ObjectMeshDynamic node)
	MeshPtr mesh = Mesh::create();
	mesh->addBoxSurface("box_surface", Math::vec3(1.5f, 1.5f, 1.5f));
	ObjectMeshDynamicPtr my_mesh = ObjectMeshDynamic::create(mesh);
	my_mesh->release();
	editor->addNode(my_mesh->getNode(), 1);
	mesh->clear();
	
	// getting the interface to material libraries
	Materials *materials = Materials::get();
	
	// creating a new material library
	materials->create("my_material_lib");

	// creating a new child material of the mesh_base and putting it to the created library
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base0");
	
	// getting a pointer to the new material named "my_mesh_base0"
	MaterialPtr  my_mesh_base = materials->findMaterial("my_mesh_base0");
	
	// setting the albedo color of the material to red
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 0, 0, 255));

	// assigning a "my_mesh_base0" material to the surface 0 of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterial("my_mesh_base0", 0);
	
	// assigning a "my_mesh_base0" material to all surfaces of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterial("my_mesh_base0", "*");


}

int AppWorldLogic::shutdown() 
{
	// deleting the material named "my_mesh_base0"
	materials->removeMaterial("my_mesh_base0");

	// clearing the library "my_material_lib"
	materials->clear("my_material_lib");

	return 1;
}

Additional information:

Project Progress

In our project we are going to create four materials inherited from the mesh_base material (each with their own albedo color) and store them in a new separate library. We are also going to need a method deleting the created materials. So, let us add methods called initMaterials and clearMaterials.

In the AppWorldLogic.h file, we include the UnigineMaterials.h library, define a smart pointer to materials interface, declare our initMaterials and clearMaterials methods.

Source code (C++)
// AppWorldLogic.h

/* .. */

#include <UnigineMaterials.h>

/* ... */

class AppWorldLogic : public Unigine::WorldLogic {
	
public:

/* .. */

private:

/* .. */

	int initMaterials();
	int clearMaterials();

/* .. */

	// pointer to the list of material libraries
	Unigine::Materials *materials;
	
/* .. */
};

In the AppWorldLogic.cpp file let us implement our declared methods and insert initMaterials into the AppWorldLogic::init() method. We are also going to change initObjects a little bit by adding names of new materials when calling the addMeshToScene method. And of course perfom a cleanup by adding a call to clearMaterials to the AppWorldLogic::shutdown() method.

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

/* .. */

/// method performing initialization of materials
int AppWorldLogic::initMaterials()
{
	// getting a pointer to materials interface
	materials = Materials::get();
	
	// creating a new material library
	materials->create("my_material_lib");

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base0");
	MaterialPtr  my_mesh_base = materials->findMaterial("my_mesh_base0");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 0, 0, 255));

	// reporting progress to the console
	Log::message("\n-> Generated %s material.\n", my_mesh_base->getName());

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base1");
	my_mesh_base = materials->findMaterial("my_mesh_base1");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base2");
	my_mesh_base = materials->findMaterial("my_mesh_base2");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 0, 255, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base3");
	my_mesh_base = materials->findMaterial("my_mesh_base3");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());
	Log::warning("Material generation OK!\n\n");

	return 1;
}

/* .. */

/// method performing initialization of objects
int AppWorldLogic::initObjects()
{
	int index = 0;
	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 2; y++)
		{
			addMeshToScene(NULL, String::format("my_meshdynamic_%d", index).get(), String::format("my_mesh_base%d", index).get(), Math::Vec3(x, y, 1.0f));
			index++;
		}
	}
	// reporting progress to the console
	Log::warning("Objects generation OK!\n\n");
	
	return 1;
}

/* .. */
	
int AppWorldLogic::init() 
{

/* .. */

	// Creating materials
	initMaterials();

/* .. */
	
	return 1;
}

/* .. */

/// method removing all materials we created
int AppWorldLogic::clearMaterials()
{
	materials->removeMaterial("my_mesh_base0");
	materials->removeMaterial("my_mesh_base1");
	materials->removeMaterial("my_mesh_base2");
	materials->removeMaterial("my_mesh_base3");

	return 1;
}

int AppWorldLogic::shutdown() {

/* .. */
	clearMaterials();
	
	return 1;
}

/* .. */

Source Files

You can copy the code below and paste it to the corresponding source files of your project:

AppWorldLogic.h

Source code (C++)
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__

#include <UnigineLogic.h>
#include <UnigineStreams.h>
#include <UnigineObjects.h>
#include <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineLights.h>
#include <UnigineMaterials.h>

class AppWorldLogic : public Unigine::WorldLogic {
	
public:
	AppWorldLogic();
	virtual ~AppWorldLogic();
	
	virtual int init();
	
	virtual int update();
	virtual int render();
	virtual int flush();
	
	virtual int shutdown();
	virtual int destroy();
	
	virtual int save(const Unigine::StreamPtr &stream);
	virtual int restore(const Unigine::StreamPtr &stream);
private:
	Unigine::Editor *editor;
	Unigine::Materials *materials;
	Unigine::PlayerSpectatorPtr player;

	// pointers to light sources
	Unigine::LightWorldPtr thesun;
	Unigine::LightOmniPtr light_omni;
	Unigine::LightProjPtr projector;

	// auxiliary functions
	int addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Unigine::Math::Vec3 position);

	// initialization functions
	int initObjects();
	int initPlayer();
	int initLights();
	int initMaterials();

	// shutdown functions
	int clearMaterials();

	// scene objects vector
	Unigine::Vector <Unigine::ObjectMeshDynamicPtr> Objects;
};

#endif // __APP_WORLD_LOGIC_H__

AppWorldLogic.cpp

Source code (C++)
#include "AppWorldLogic.h"
// World logic, it takes effect only when the world is loaded.
// These methods are called right after corresponding world script's (UnigineScript) methods.
using namespace Unigine;
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- AUXILIARY FUNCTIONS AND METHODS ----------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------

/// method adding a Dynamic Mesh Object to the scene. If an empty filename is passed the method creates a default box; otherwise, loads a mesh-file with a given name.
int AppWorldLogic::addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Math::Vec3 position)
{
	MeshPtr mesh = Mesh::create();
	ObjectMeshDynamicPtr omd;
	if (filename){				// loading a mesh from a specified file
		if (!mesh->load(filename))
		{
			Log::error("\nError opening .mesh file!\n");
			mesh.clear();
			
			return 0;
		}
		else omd = ObjectMeshDynamic::create(mesh);
	}
	else																// creating a default box
	{
		mesh->addBoxSurface("box_surface", Math::vec3(0.5f));

		omd = ObjectMeshDynamic::create(mesh);
	}

	// setting node material, name and position
	omd->setMaterial(material_name, "*");
	omd->setName(mesh_name);
	omd->setWorldPosition(position);
	
	// passing node ownership to the editor as a runtime node
	omd->release();
	editor->addNode(omd->getNode());

	// updating the list of scene objects
	Objects.append(omd);

	// reporting progress to the console
	Log::message("-> Object %s added to the scene.\n", mesh_name);

	// clearing the mesh
	mesh->clear();

	return 1;

}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- INITIALIZATION METHODS -------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of the set of 4 boxes
int AppWorldLogic::initObjects()
{
	int index = 0;

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 2; y++)
		{
			addMeshToScene(NULL, String::format("my_meshdynamic_%d", index).get(), String::format("my_mesh_base%d", index).get(), Math::Vec3(x, y, 1.0f));
			index++;
		}
	}

	// reporting progress to the console
	Log::warning("Objects generation OK!\n\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of the player
int AppWorldLogic::initPlayer()
{
	// creating a new PlayerSpectator instance
	player = PlayerSpectator::create();

	// setting player's FOV, ZNear, ZFar
	player->setFov(90.0f);
	player->setZNear(0.1f);
	player->setZFar(10000.0f);

	// setting player's view direction vector and position
	player->setPosition(Math::Vec3(3.0f));
	player->setDirection(Math::vec3(-1.0f), Math::vec3(0.0f, 0.0f, -1.0f));

	// setting the player to the Game singleton instance
	Game::get()->setPlayer(player->getPlayer());
	player->release();

	//reporting progress to the console
	Log::warning("\nPlayer initialization OK!\n\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of lights
int AppWorldLogic::initLights()
{
	// creating an omni light and setting up its parameters
	light_omni = LightOmni::create(Math::vec4(1.0f, 1.0f, 1.0f, 1.0f), 10.0f, "");
	light_omni->setWorldPosition(Math::Vec3(0.0f, 0.0f, 5.0f));
	light_omni->setIntensity(0.1f);

	// passing node ownership to the editor
	light_omni->release();
	editor->addNode(light_omni->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", light_omni->getName());

	// creating a world light and setting up its parameters
	thesun = LightWorld::create(Math::vec4(1.0f, 1.0f, 1.0f, 1.0f));
	thesun->setName("Sun");
	thesun->setDisableAngle(90.0f);
	thesun->setIntensity(1.0f);
	thesun->setScattering(LightWorld::SCATTERING_SUN);
	thesun->setWorldRotation(Math::quat(0.0f, 1.0f, 0.0f, 170.0f));

	// passing node ownership to the editor
	thesun->release();
	editor->addNode(thesun->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", thesun->getName());

	// creating a proj light and setting up its parameters
	projector = LightProj::create(Math::vec4(1.0f, 1.0f, 0.5f, 1.0f), 10.0f, 60.0f, "");
	projector->setWorldPosition(Math::Vec3(2.5f, 2.5f, 3.0f));
	projector->setName("projector");
	projector->setRotation(Math::quat(-45.0f, 45.0f, 0.0f));
	projector->setPenumbra(0.425f);
	projector->setIntensity(1.0f);

	// passing node ownership to the editor
	projector->release();
	editor->addNode(projector->getNode());

	// reporting progress to the console
	Log::message("-> Created a %s light source.\n", projector->getName());
	Log::warning("Lights initialization OK!\n");

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of materials
int AppWorldLogic::initMaterials()
{
	// getting a pointer to materials interface
	materials = Materials::get();

	// creating a new material library
	materials->create("my_material_lib");

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base0");
	MaterialPtr  my_mesh_base = materials->findMaterial("my_mesh_base0");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 0, 0, 255));

	// reporting progress to the console
	Log::message("\n-> Generated %s material.\n", my_mesh_base->getName());

	// creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base1");
	my_mesh_base = materials->findMaterial("my_mesh_base1");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base2");
	my_mesh_base = materials->findMaterial("my_mesh_base2");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(0, 0, 255, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());

	//creating a new child material of the mesh_base and setting its color
	materials->inheritMaterial("mesh_base", "my_material_lib", "my_mesh_base3");
	my_mesh_base = materials->findMaterial("my_mesh_base3");
	my_mesh_base->setParameter(Material::PARAMETER_COLOR, Math::vec4(255, 255, 0, 255));

	// reporting progress to the console
	Log::message("-> Generated %s material.\n", my_mesh_base->getName());
	Log::warning("Material generation OK!\n\n");

	// clearing a material pointer
	my_mesh_base.clear();

	return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- SHUTDOWN METHODS -------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method removing all created materials
int AppWorldLogic::clearMaterials()
{
	materials->removeMaterial("my_mesh_base0");
	materials->removeMaterial("my_mesh_base1");
	materials->removeMaterial("my_mesh_base2");
	materials->removeMaterial("my_mesh_base3");

	return 1;
}

AppWorldLogic::AppWorldLogic() {
	
}

AppWorldLogic::~AppWorldLogic() {
	
}

int AppWorldLogic::init() {
	// Write here code to be called on world initialization: initialize resources for your world scene during the world start.
	
	// getting a pointer to the Editor
	editor = Editor::get();

	// creating materials
	initMaterials();

	// creating objects
	initObjects();

	// creating a player
	initPlayer();

	// creating lights
	initLights();
	
	return 1;
}

// 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::render() {
	// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.
	
	return 1;
}

int AppWorldLogic::flush() {
	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
	// The engine calls flush() 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.

	// deleting all created nodes
	for(int i = 0; i < Objects.size(); i++)
	{
		// removing current node from the scene
		editor->removeNode(Objects[i]->get()->getNode());
	}
	
	// updating the list of scene objects
	Objects.clear();

	// clearing the player pointer
	player.clear();

	// clearing light sources
	thesun.clear();
	light_omni.clear();
	projector.clear();

	// clearing all created materials
	clearMaterials();

	return 1;
}

int AppWorldLogic::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;
}

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;
}

PROCEED TO THE NEXT SECTION >>

Last update: 2017-07-03