5. 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 loaded materials.
- Material class which is used to manage each individual material.
The following code can be used to create a new material inherited from the mesh_base material.
#include <core/unigine.h>
int init() {
// creating a box (ObjectMeshDynamic node)
Mesh mesh = new Mesh();
mesh.addBoxSurface("box_surface", vec3(1.5f, 1.5f, 1.5f));
ObjectMeshDynamic my_mesh = new ObjectMeshDynamic(mesh);
// getting the base mesh_base material to inherit from
Material mesh_base = engine.materials.findMaterial("mesh_base");
// creating a new child material of the mesh_base named "my_mesh_base0"
Material my_mesh_base = mesh_base.inherit("my_mesh_base0");
// setting the albedo color of the material to red
my_mesh_base.setParameter("albedo_color", 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", "*");
return 1;
}
int shutdown() {
// deleting the material named "my_mesh_base0"
engine.materials.removeMaterial(engine.materials.findMaterial("my_mesh_base0").getGUID());
return 1;
}
Additional information:
- For more information on creating and editing materials via API, see the Material class article.
- For more information on managing loaded materials via API, see the Materials class article.
- For more information on materials files formats, see the Materials Files section.
- For more information on materials parameters, see the materials files in the %UNIGINE_SDK_BROWSER_INSTALLATION_FOLDER%/sdks/%CURRENT_SDK%/data/core/materials/default/ folder.
Project Progress#
In our project we are going to create four materials inherited from the mesh_base material (each with their own albedo color). 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, declare our initMaterials and clearMaterials methods.
// AppWorldLogic.h
/* .. */
#include <UnigineMaterials.h>
/* ... */
class AppWorldLogic : public Unigine::WorldLogic {
public:
/* .. */
private:
/* .. */
int initMaterials();
int clearMaterials();
/* .. */
};
In the AppWorldLogic.cpp file let us implement our declared methods and insert initMaterials into the AppWorldLogic::init() method before initialization of objects. We are also going to change initObjects a little bit by adding names of new materials when calling the addMeshToScene method. And of course perform a cleanup by adding a call to clearMaterials to the AppWorldLogic::shutdown() method.
// AppWorldLogic.cpp
/* .. */
/// method performing initialization of materials
int AppWorldLogic::initMaterials()
{
// creating a new child material of the mesh_base and setting its color
MaterialPtr mesh_base = Materials::findMaterial("mesh_base");
MaterialPtr my_mesh_base = mesh_base->inherit("my_mesh_base0");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base1");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base2");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base3");
my_mesh_base->setParameterFloat4("albedo_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), String::format("my_mesh_base%d", index), 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(Materials::findMaterial("my_mesh_base0")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base1")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base2")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base3")->getGUID());
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
#ifndef __APP_WORLD_LOGIC_H__
#define __APP_WORLD_LOGIC_H__
#include <UnigineLogic.h>
#include <UnigineStreams.h>
#include <UnigineObjects.h>
#include <UnigineGame.h>
#include <UnigineLights.h>
#include <UnigineMaterials.h>
using namespace Unigine;
class AppWorldLogic : public WorldLogic {
public:
AppWorldLogic();
virtual ~AppWorldLogic();
virtual int init();
virtual int update();
virtual int postUpdate();
virtual int updatePhysics();
virtual int shutdown();
virtual int save(const StreamPtr &stream);
virtual int restore(const StreamPtr &stream);
private:
PlayerSpectatorPtr player;
// pointers to light sources
LightWorldPtr thesun;
LightOmniPtr light_omni;
LightProjPtr projector;
// auxiliary functions
int addMeshToScene(const char *file_name, const char *mesh_name, const char *material_name, Math::Vec3 position);
// initialization functions
int initObjects();
int initPlayer();
int initLights();
int initMaterials();
// shutdown functions
int clearMaterials();
// scene objects vector
Vector <ObjectMeshDynamicPtr> Objects;
};
#endif // __APP_WORLD_LOGIC_H__
AppWorldLogic.cpp
#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.
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- 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 (file_name) { // loading a mesh from a specified file
if (!mesh->load(file_name))
{
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);
// 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), String::format("my_mesh_base%d", index), 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 as a default one via the Game singleton instance
Game::setPlayer(player);
//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);
// 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(86.0f, 30.0f, 300.0f));
// 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);
// 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()
{
// creating a new child material of the mesh_base and setting its color
MaterialPtr mesh_base = Materials::findMaterial("mesh_base");
MaterialPtr my_mesh_base = mesh_base->inherit("my_mesh_base0");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base1");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base2");
my_mesh_base->setParameterFloat4("albedo_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
my_mesh_base = mesh_base->inherit("my_mesh_base3");
my_mesh_base->setParameterFloat4("albedo_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(Materials::findMaterial("my_mesh_base0")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base1")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base2")->getGUID());
Materials::removeMaterial(Materials::findMaterial("my_mesh_base3")->getGUID());
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.
// 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::postUpdate() {
// 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::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.
// deleting all created nodes
for (int i = 0; i < Objects.size(); i++)
{
// removing current node from the scene
Objects[i]->deleteLater();
}
// updating the list of scene objects
Objects.clear();
// clearing the player pointer
player->deleteLater();
// clearing light sources
thesun->deleteLater();
light_omni->deleteLater();
projector->deleteLater();
// clearing all created materials
clearMaterials();
return 1;
}
int AppWorldLogic::save(const 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 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;
}