16. Creating User Interface
<< RETURN TO THE PREVIOUS SECTION
In UNIGINE a Graphical User Interface (GUI) is composed of different types of widgets added to it. Basically, there are two ways of creating GUI:
- By adding widgets to the system GUI (Unigine user interface) that is rendered on top of application window.
- By adding widgets to a GUI object positioned in the world. In this case, any postprocessing filter can be applied.
There are 2 ways to create the GUI layout:
- Directly from the code via GUI-related classes
- By using User interface (UI) files
#include <UnigineUserInterface.h>
using namespace Unigine;
GuiPtr gui;
int AppWorldLogic::init()
{
// getting a GUI pointer
gui = Gui::get();
// creating a label widget and setting up its parameters
WidgetLabelPtr widget_label = WidgetLabel::create(gui, "Label text:");
widget_label->setToolTip("This is my label!");
widget_label->arrange();
widget_label->setPosition(10, 10);
// creating a slider widget and setting up its parameters
WidgetSliderPtr widget_slider = WidgetSlider::create(gui, 0, 360, 90);
widget_slider->setToolTip("This is my slider!");
widget_slider->arrange();
widget_slider->setPosition(100, 10);
}
In order to use GUI elements we must specify handlers for various events (click, change, etc.). The following code demonstrates how to set event handlers
#include <UnigineUserInterface.h>
using namespace Unigine;
/// function to be called when button1 is clicked
int onButton1Clicked()
{
/* .. */
}
/// method to be called when button2 is clicked
int AppWorldLogic::onButton2Clicked()
{
/* .. */
}
/// method to be called when slider position is changed
int AppWorldLogic::onSliderChanged()
{
/* .. */
}
int AppWorldLogic::init()
{
/* .. */
// getting a GUI pointer
gui = Gui::get();
// setting onButton1Clicked function as a clicked event handler for a buttonwidget2 using a CallbackBase variable
Unigine::CallbackBase *button1_clicked_callback_function = MakeCallback(onButton1Clicked);
buttonwidget1->setCallback0(Gui::CLICKED, button1_clicked_callback_function);
// setting AppWorldLogic::onButton2Clicked method as a clicked event handler for a buttonwidget2 using a CallbackBase variable
Unigine::CallbackBase *button2_clicked_callback_method = MakeCallback(this, &AppWorldLogic::onButton2Clicked);
buttonwidget2->setCallback0(Gui::CLICKED, button2_clicked_callback_method);
buttonwidget1->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));
// setting AppWorldLogic::onSliderChanged method as a changed event handler for a widget_slider
widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));
/* .. */
}
Additional information:
- For more information on UI files, see UI files page.
- For more information on GUI API, see GUI-related classes page.
- For more information on the Gui class, see Gui class page.
Project Progress
In our project we are going to add the following widgets to the system GUI:
- Label (WidgetLabel) to display the name of the parameter controlled by the slider.
- Slider (WidgetSlider) to control the position of the sun manually.
- Checkbox (WidgetCheckbox) to enable/disable automatic change of the sun position.
- 4 Buttons (WidgetButton): Close Application, Load World, Load Mesh, and Delete.
- ComboBox (WidgetComboBox) to display the list of scene objects.
- File Dialog (WidgetDialogFile) to load a mesh from a file.
- Window (Widget) to set mesh parameters.
We are going to create all widgets directly from the code, but the last one (Mesh parameters window) we are going to load from a UI file. For this purpose let us create a file with the following contents and save it to the data folder of our application as "user_interface.ui"
<?xml version="1.0" encoding="utf-8"?>
<ui version="1.0">
<window name="window" export="1" space="4" sizeable="1" width="512">
<text>UserInterface</text>
<gridbox columns="2" space="8" align="expand">
<label name="label_n" align="expand">
<text>Name:</text>
</label>
<editline name="object_name" align="expand">
<text></text>
</editline>
<label name="label_x" align="expand">
<text>X coordinate:</text>
</label>
<editline name="coord_x" align="expand">
<text></text>
</editline>
<label name="label_y" align="expand">
<text>Y coordinate:</text>
</label>
<editline name="coord_y" align="expand">
<text></text>
</editline>
<label name="label_z" align="expand">
<text>Z coordinate:</text>
</label>
<editline name="coord_z" align="expand">
<text></text>
</editline>
</gridbox>
<button name="button_OK" align="bottom">
<text>OK</text>
</button>
</window>
</ui>
To create our GUI let us write event handler methods for the elements of our UI and a method called initGUI to initialize our UI.
In the AppWorldLogic.h file, we:
- include the UnigineUserInterface.h library,
- define smart pointers for the widgets we are going to use,
- declare event handlers for each widget and our initGUI method.
// AppWorldLogic.h
/* .. */
#include <UnigineUserInterface.h>
/* .. */
class AppWorldLogic : public Unigine::WorldLogic {
public:
/* .. */
// GUI event handlers
int onButtonDelClicked();
int onButtonCloseClicked();
int onButtonFopenClicked();
int onButtonWloadClicked();
int onDlgCancelClicked();
int onDlgOKClicked();
int onMeshParamsOKClicked();
int onSliderChanged();
/* .. */
private:
/* .. */
// creating GUI
int initGUI();
/* .. */
// pointers to the UI and its elements
Unigine::GuiPtr gui;
Unigine::UserInterfacePtr ui;
Unigine::WidgetLabelPtr widget_label;
Unigine::WidgetSliderPtr widget_slider;
Unigine::WidgetCheckBoxPtr widget_checkbox;
Unigine::WidgetButtonPtr widget_button_del;
Unigine::WidgetButtonPtr widget_button_close;
Unigine::WidgetButtonPtr widget_button_fopen;
Unigine::WidgetButtonPtr widget_button_wload;
Unigine::WidgetComboBoxPtr widget_combo;
Unigine::WidgetDialogFilePtr widget_file_dialog;
Unigine::WidgetPtr mesh_parameters_window;
/* .. */
};
In the AppWorldLogic.cpp file let us:
- implement all our event handlers,
- implement the initGUI method and insert it into the AppWorldLogic::init() method,
- make some changes to the following methods in order to integrate our UI:
- updateLights (check if automatic mode is enabled to change sun position),
- addMeshToScene (add the name of the new created object to the combo box an enable Delete button if it was disabled),
- removeMeshFromScene (update the combo box and disable Delete button if the last object was deleted).
// AppWorldLogic.cpp
/* .. */
//-----------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------- GUI EVENT HANDLERS --------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Close button is clicked in the main window
int AppWorldLogic::onButtonCloseClicked()
{
// removing generated materials
clearMaterials();
// saving current world to my_world.world file
Console::get()->run("world_save my_world");
// executing all pending console commands before exiting
Console::get()->flush();
// closing the application
App::get()->exit();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Load world button is clicked in the main window
int AppWorldLogic::onButtonWloadClicked()
{
// reporting to console
Log::warning("\n\n --- RELOADING A DEFAULT WORLD --- \n\n");
// loading a world from default.world file
Console::get()->run("world_load default");
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the main window
int AppWorldLogic::onButtonFopenClicked()
{
widget_file_dialog->setHidden(0);
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the slider position is changed in the main window
int AppWorldLogic::onSliderChanged()
{
// updating the position of the Sun
sun_angle = widget_slider->getValue();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Delete button is clicked in the main window
int AppWorldLogic::onButtonDelClicked()
{
String node_name;
if (widget_combo->getCurrentItem() == 0) // deleting all objects
{
removeObjects();
// disabling Delete button in the main window
widget_button_del->setEnabled(0);
}
else // deleting an object selected in the combo box
{
// retrieving currently selected node name in the combo box
node_name = widget_combo->getCurrentItemText();
// checking if a node with a given name exists and removing it from the scene
if (editor->findNode(node_name.get()) != -1)
{
removeMeshFromScene(node_name.get());
}
}
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the File Open dialog
int AppWorldLogic::onDlgCancelClicked()
{
// hiding the file selecton dialog
widget_file_dialog->setHidden(1);
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the File Open dialog
int AppWorldLogic::onDlgOKClicked()
{
// checking if the file exists
if (FILE *file = fopen(widget_file_dialog->getFile(), "r")) {
fclose(file);
//setting initial values of the fields in the Mesh Parameters window
String object_name = String::format("Object_%d", widget_combo->getNumItems() - 1);
WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")))->setText(object_name.get());
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")))->setText("0.0");
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")))->setText("0.0");
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")))->setText("0.0");
// hiding the File Open dialog and showing Mesh Parameters window using different ways
widget_file_dialog->setHidden(1);
mesh_parameters_window->setHidden(0);
return 1;
}
// reporting an error to the console
Log::error("Loading mesh: can't open \"%s\" file\n", widget_file_dialog->getFile());
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the Mesh Parameters window
int AppWorldLogic::onMeshParamsOKClicked()
{
//retrieving pointers to UI elements
WidgetEditLinePtr name = WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")));
WidgetEditLinePtr x = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")));
WidgetEditLinePtr y = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")));
WidgetEditLinePtr z = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")));
if ((x->getText() != "") && (y->getText() != "") && (z->getText() != "") && (name->getText() != ""))
{
Math::Vec3 pos = Math::Vec3(atof(x->getText()), atof(y->getText()), atof(z->getText()));
String result = String((addMeshToScene(widget_file_dialog->getFile(), name->getText(), "my_mesh_base3", pos)) ? "Ok!" : "Error!");
// reporting progress to the console
Log::message("(%s) mesh load operation status: %s\n", name->getText(), result.get());
// hiding the Mesh Parameters window
mesh_parameters_window->setHidden(1);
return 1;
}
Log::error("Wrong mesh parameters!\n");
return 0;
}
/* .. */
/// method performing initialization of the GUI
int AppWorldLogic::initGUI()
{
// getting a GUI pointer
gui = Gui::get();
// creating a label widget and setting up its parameters
widget_label = WidgetLabel::create(gui, "Sun position:");
widget_label->setToolTip("Change sun position");
widget_label->setPosition(10, 10);
// creating a slider widget and setting up its parameters
widget_slider = WidgetSlider::create(gui, 0, 360, 90);
widget_slider->setToolTip("Change sun position");
widget_slider->setPosition(100, 10);
widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));
// creating a checkbox widget and setting up its parameters
widget_checkbox = WidgetCheckBox::create(gui, "Automatic change");
widget_checkbox->setToolTip("Change sun position automatically");
widget_checkbox->setPosition(10, 30);
widget_checkbox->setChecked(1);
// creating a button widget and setting up its parameters
widget_button_del = WidgetButton::create(gui, "Delete");
widget_button_del->setToolTip("Delete object(s) selected in the combo box");
widget_button_del->setPosition(170, 50);
widget_button_del->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));
// creating a button widget and setting up its parameters
widget_button_fopen = WidgetButton::create(gui, "Load mesh");
widget_button_fopen->setToolTip("Load a mesh file");
widget_button_fopen->setPosition(10, 80);
widget_button_fopen->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonFopenClicked));
// creating a button widget and setting up its parameters
widget_button_close = WidgetButton::create(gui, "Quit");
widget_button_close->setToolTip("Close application");
widget_button_close->setPosition(10, 110);
widget_button_close->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonCloseClicked));
// creating a button widget and setting up its parameters
widget_button_wload = WidgetButton::create(gui, "Load world");
widget_button_wload->setToolTip("Load a default world");
widget_button_wload->setPosition(110, 110);
widget_button_wload->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonWloadClicked));
// creating a combo box widget and setting up its parameters
widget_combo = WidgetComboBox::create(gui);
widget_combo->addItem("----ALL-----");
widget_combo->setPosition(10, 50);
// creating a File Selection dialog widget and setting up its parameters
widget_file_dialog = WidgetDialogFile::create(gui, "Select a mesh file");
widget_file_dialog->setPath("../");
widget_file_dialog->setHidden(1);
widget_file_dialog->setFilter(".mesh");
widget_file_dialog->getCancelButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgCancelClicked));
widget_file_dialog->getOkButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgOKClicked));
// creating a Mesh Parameters dialog from an external ui-file
ui = UserInterface::create(gui, "user_interface.ui");
ui->setCallback0("button_OK", Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onMeshParamsOKClicked));
mesh_parameters_window = ui->getWidget(ui->findWidget("window"));
mesh_parameters_window->setHidden(1);
// adding created widgets to the UI
gui->addChild(widget_label->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 0 - label
gui->addChild(widget_slider->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 1 - slider
gui->addChild(widget_checkbox->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 2 - checkbox
gui->addChild(widget_button_del->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 3 - delete button
gui->addChild(widget_button_close->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 4 - close button
gui->addChild(widget_combo->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 5 - combo box
gui->addChild(widget_button_fopen->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 6 - Load Mesh button
gui->addChild(widget_button_wload->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 7 - Load world button
gui->addChild(mesh_parameters_window, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER); // ID: 8 - Mesh Parameters window
gui->addChild(widget_file_dialog->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER); // ID: 9 - File Open dialog
//reporting progress to the console
for (int k = 0; k < gui->getNumChildren() ; k++)
{
Log::message("-> Adding UI element %d: %s.\n", k, gui->getChild(k)->getTypeName());
}
Log::warning("UI initialized! Number of elements: %d.\n", gui->getNumChildren());
return 1;
}
/* .. */
int AppWorldLogic::init()
{
/* .. */
// creating GUI
initGUI();
/* .. */
return 1;
}
/* .. */
/// method updating positions of lights
int AppWorldLogic::updateLights()
{
// updating the Sun position if automatic mode is enabled
if (widget_checkbox->isChecked())
{
sun_angle += SUN_ROTATION_RATE * ifps;
if (sun_angle > 360.0f) sun_angle = 0.0f;
// updating slider position
widget_slider->setValue(sun_angle);
}
// updating the Sun position
LightWorld::cast(editor->getNodeByName("Sun"))->setWorldRotation(Math::quat(Math::vec3(0.0f, 1.0f, 0.0f), 180.0f - sun_angle));
return 1;
}
/* .. */
//-----------------------------------------------------------------------------------------------------------------------------
/// method adding a Dynamic Mesh Object to the scene. If an empty filename is passed the function creates a default box
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, 0.5f, 0.5f));
omd = ObjectMeshDynamic::create(mesh);
}
// setting object's material, name and transformation
omd->setMaterial(material_name, "*");
omd->setName(mesh_name);
omd->setWorldPosition(position);
// inheriting a new property from surface_base_1
String new_property_name = String::format("%s_property", mesh_name);
properties->findManualProperty("surface_base_1")->inherit(new_property_name.get());
// assigning the new property to the node and setting values of its parameters
omd->setProperty(0,new_property_name.get());
omd->getProperty(0)->setParameterString(omd->getProperty(0)->findParameter("material"), material_name);
omd->getProperty(0)->setParameterInt(omd->getProperty(0)->findParameter("my_parameter"), 0);
// enabling intersection detection for the first surface (0)
omd->setIntersection(1, 0);
// passing node ownership to the editor
omd->release();
editor->addNode(omd->getNode());
// updating combo list and the list of scene objects
widget_combo->addItem(omd->getName());
Objects.append(omd);
// enabling Delete button in the main window
widget_button_del->setEnabled(1);
//reporting progress to the console
Log::message("-> Object %s added to the scene.\n", mesh_name);
// clearing the mesh
mesh->clear();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method deleting a Dynamic Mesh Object with a given name from the scene
int AppWorldLogic::removeMeshFromScene(const char *node_name)
{
// getting a pointer to the node with a given name and downcasting it to ObjectMeshDynamicPtr
ObjectMeshDynamicPtr object = ObjectMeshDynamic::cast(editor->getNodeByName(node_name));
if (object)
{
// checking if the node to be removed was previously selected and resetting old_selection pointer
if (old_selection)
if (object->getNode() == old_selection->getNode())
old_selection = ObjectPtr(NULL);
// reporting node deletion to the console
Log::message("Removing %s node named %s from the scene.\n", object->getTypeName(), node_name);
// removing the node with a given name from the list of scene objects and updating the combobox
for (int i = 0; i < Objects.size();i++)
{
if (strcmp(Objects[i]->getName(), node_name) == 0)
{
Objects.remove(i);
widget_combo->removeItem(i + 1);
break;
}
}
// removing the node from the scene using upcasting
editor->removeNode(object->getNode());
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
int AppWorldLogic::shutdown() {
// resetting selected object pointer
old_selection = ObjectPtr(NULL);
// removing generated materials
clearMaterials();
// removing all objects from the scene
removeObjects();
// clearing the list of scene objects
Objects.clear();
// clearing gui elements
gui.clear();
// removing player
player.clear();
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 <UnigineEditor.h>
#include <UnigineGame.h>
#include <UnigineLights.h>
#include <UnigineMaterials.h>
#include <UnigineApp.h>
#include <UnigineConsole.h>
#include <UnigineUserInterface.h>
// auxiliary constants
const float DELTA_ANGLE = 60.0f; // delta angle of objects rotation
const float MOVING_SPEED = 3.0f; // speed of objects movement
const float CHANGE_INTERVAL = 1.0f; // the interval between changes of objects' parameters, in seconds
const float SUN_ROTATION_RATE = 10.0f; // rotation rate of the sun
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);
// GUI event handlers
int onButtonDelClicked();
int onButtonCloseClicked();
int onButtonFopenClicked();
int onButtonWloadClicked();
int onDlgCancelClicked();
int onDlgOKClicked();
int onMeshParamsOKClicked();
int onSliderChanged();
private:
Unigine::Editor *editor;
Unigine::App *app;
Unigine::Materials *materials;
Unigine::Properties *properties;
Unigine::PlayerSpectatorPtr player;
// pointers to the GUI elements
Unigine::GuiPtr gui;
Unigine::UserInterfacePtr ui;
Unigine::WidgetLabelPtr widget_label;
Unigine::WidgetSliderPtr widget_slider;
Unigine::WidgetCheckBoxPtr widget_checkbox;
Unigine::WidgetButtonPtr widget_button_del;
Unigine::WidgetButtonPtr widget_button_close;
Unigine::WidgetButtonPtr widget_button_fopen;
Unigine::WidgetButtonPtr widget_button_wload;
Unigine::WidgetComboBoxPtr widget_combo;
Unigine::WidgetDialogFilePtr widget_file_dialog;
Unigine::WidgetPtr mesh_parameters_window;
// 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);
int removeMeshFromScene(const char *node_name);
int transformNode(Unigine::NodePtr node, float ifps);
// initialization functions
int initObjects();
int initPlayer();
int initLights();
int initMaterials();
int initProperties();
int initGUI();
// update functions
int updateLights();
int updateObjects();
// shutdown functions
int clearMaterials();
int removeObjects();
// scene objects vector
Unigine::Vector <Unigine::ObjectMeshDynamicPtr> Objects;
Unigine::ObjectPtr old_selection; // pointer to previously selected object
Unigine::Math::vec3 current_objects_scale = Unigine::Math::vec3(1.0f); // current scaling vector for objects
Unigine::Math::Vec3 forward_direction = Unigine::Math::Vec3(0.0f, -1.0f, 0.0f); // current forward direction vector for objects
float elapsed_time = CHANGE_INTERVAL; // current time left to change current scale and forward direction of our objects
float sun_angle = 0.0f; // current sun position
};
#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.
using namespace Unigine;
// auxiliary variable to store the time of rendering the last frame
float ifps;
//-----------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------- GUI EVENT HANDLERS --------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Close button is clicked in the main window
int AppWorldLogic::onButtonCloseClicked()
{
// removing generated materials and properties
clearMaterials();
// saving current world to my_world.world file
Console::get()->run("world_save my_world");
// executing all pending console commands before exiting
Console::get()->flush();
// closing the application
App::get()->exit();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Load world button is clicked in the main window
int AppWorldLogic::onButtonWloadClicked()
{
// reporting to console
Log::warning("\n\n --- RELOADING A DEFAULT WORLD --- \n\n");
// loading a world from default.world file
Console::get()->run("world_load default");
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the main window
int AppWorldLogic::onButtonFopenClicked()
{
widget_file_dialog->setHidden(0);
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the slider position is changed in the main window
int AppWorldLogic::onSliderChanged()
{
// updating the position of the Sun
sun_angle = widget_slider->getValue();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Delete button is clicked in the main window
int AppWorldLogic::onButtonDelClicked()
{
String node_name;
if (widget_combo->getCurrentItem() == 0) // deleting all objects
{
removeObjects();
// disabling Delete button in the main window
widget_button_del->setEnabled(0);
}
else // deleting an object selected in the combo box
{
// retrieving currently selected node name in the combo box
node_name = widget_combo->getCurrentItemText();
// checking if a node with a given name exists and removing it from the scene
if (editor->findNode(node_name.get()) != -1)
{
removeMeshFromScene(node_name.get());
}
}
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the Cancel button is clicked in the File Open dialog
int AppWorldLogic::onDlgCancelClicked()
{
// hiding the file selecton dialog
widget_file_dialog->setHidden(1);
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the File Open dialog
int AppWorldLogic::onDlgOKClicked()
{
// checking if the file exists
if (FILE *file = fopen(widget_file_dialog->getFile(), "r")) {
fclose(file);
//setting initial values of the fields in the Mesh Parameters window
String object_name = String::format("Object_%d", widget_combo->getNumItems() - 1);
WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")))->setText(object_name.get());
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")))->setText("0.0");
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")))->setText("0.0");
WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")))->setText("0.0");
// hiding the File Open dialog and showing Mesh Parameters window using different ways
widget_file_dialog->setHidden(1);
mesh_parameters_window->setHidden(0);
return 1;
}
// reporting an error to the console
Log::error("Loading mesh: can't open \"%s\" file\n", widget_file_dialog->getFile());
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method to be called when the OK button is clicked in the Mesh Parameters window
int AppWorldLogic::onMeshParamsOKClicked()
{
//retrieving pointers to UI elements
WidgetEditLinePtr name = WidgetEditLine::cast(ui->getWidget(ui->findWidget("object_name")));
WidgetEditLinePtr x = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_x")));
WidgetEditLinePtr y = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_y")));
WidgetEditLinePtr z = WidgetEditLine::cast(ui->getWidget(ui->findWidget("coord_z")));
if ((x->getText() != "") && (y->getText() != "") && (z->getText() != "") && (name->getText() != ""))
{
Math::Vec3 pos = Math::Vec3(atof(x->getText()), atof(y->getText()), atof(z->getText()));
String result = String((addMeshToScene(widget_file_dialog->getFile(), name->getText(), "my_mesh_base3", pos)) ? "Ok!" : "Error!");
// reporting progress to the console
Log::message("(%s) mesh load operation status: %s\n", name->getText(), result.get());
// hiding the Mesh Parameters window
mesh_parameters_window->setHidden(1);
return 1;
}
Log::error("Wrong mesh parameters!\n");
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- 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;
String new_property_name = String::format("%s_property", mesh_name);
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);
//inheriting a new property from "surface_base_1"
properties->inheritProperty("surface_base_1", "my_property_lib.prop", new_property_name.get());
// assigning the new property to the node and setting values of its parameters
omd->setProperty(new_property_name.get(), "*");
omd->getProperty(0)->setParameterString(omd->getProperty(0)->findParameter("material"), material_name);
omd->getProperty(0)->setParameterInt(omd->getProperty(0)->findParameter("selected"), 0);
// enabling intersection detection for the first surface (0)
omd->setIntersection(1, 0);
// passing node ownership to the editor as a runtime node
omd->release();
editor->addNode(omd->getNode());
// updating combo list and the list of scene objects
widget_combo->addItem(omd->getName());
Objects.append(omd);
// enabling Delete button in the main window
widget_button_del->setEnabled(1);
// reporting progress to the console
Log::message("-> Object %s added to the scene.\n", mesh_name);
// clearing the mesh
mesh->clear();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method deleting a Dynamic Mesh Object with a given name from the scene
int AppWorldLogic::removeMeshFromScene(const char *node_name)
{
// getting a pointer to the node with a given name and downcasting it to ObjectMeshDynamicPtr
ObjectMeshDynamicPtr object = ObjectMeshDynamic::cast(editor->getNodeByName(node_name));
if (object)
{
if (old_selection)
{
// checking if the node to be removed was previously selected
if (object->getNode() == old_selection->getNode())
{
// resetting old_selection pointer to NULL
old_selection.clear();
}
}
// reporting node deletion to the console
Log::message("Removing %s node named %s from the scene.\n", object->getTypeName(), node_name);
// removing the node with a given name from the list of scene objects
for (int i = 0; i < Objects.size(); i++)
{
if (strcmp(Objects[i]->getName(), node_name) == 0) {
Objects.remove(i);
widget_combo->removeItem(i + 1);
break;
}
}
// removing the node from the scene using upcasting
editor->removeNode(object->getNode());
return 1;
}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing node transformations
int AppWorldLogic::transformNode(NodePtr node, float ifps)
{
// getting current node transformation matrix
Math::Mat4 transform = node->getTransform();
// calculating delta rotation around an arbitrary axis
Math::quat delta_rotation = Math::quat(rand() % 2, rand() % 2, rand() % 2, DELTA_ANGLE * ifps);
// setting node's scale, rotation and position
node->setWorldScale(current_objects_scale);
node->setWorldRotation(node->getWorldRotation() * delta_rotation);
node->setWorldPosition(node->getWorldPosition() + forward_direction * MOVING_SPEED *ifps);
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// function getting an object under the mouse cursor
ObjectPtr getObjectUnderMouseCursor(const PlayerPtr &player, int mouse_x, int mouse_y, float max_dist)
{
// setting start point (p0) at the center of player's screen
Math::Vec3 p0 = player->getWorldPosition();
// setting end point (p1) according to the position of the mouse cursor
Math::Vec3 p1 = p0 + Math::Vec3(player->getDirectionFromScreen(mouse_x, mouse_y)) * max_dist;
WorldIntersectionPtr intersection = WorldIntersection::create();
// returning the pointer to the first intersected object if any or NULL
return World::get()->getIntersection(p0, p1, 1, intersection);
}
//-----------------------------------------------------------------------------------------------------------------------------
/// function setting new object as selected and updating previous selection
int selectObject(ObjectPtr new_selected_object, ObjectPtr &old_selected_object)
{
// checking if new selected object is not NULL and it has a property with parameter "selected" assigned
if (new_selected_object)
{
if (new_selected_object->getProperty(0)->findParameter("selected") != -1)
{
// checking if we already have a previously selected object and setting its "selected" parameter of the property to 0
if (old_selected_object)
{
old_selected_object->getProperty(0)->setParameterInt(old_selected_object->getProperty(0)->findParameter("selected"), 0);
}
// setting the "selected" parameter of the property to 1 for the new selected object
new_selected_object->getProperty(0)->setParameterInt(new_selected_object->getProperty(0)->findParameter("selected"), 1);
// remembering the object as selected
old_selected_object = new_selected_object;
return 1;
}
}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------- 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 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->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
my_mesh_base = mesh_base->inherit("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
my_mesh_base = mesh_base->inherit("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
my_mesh_base = mesh_base->inherit("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 material pointer
my_mesh_base.clear();
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method performing initialization of properties
int AppWorldLogic::initProperties()
{
// getting a pointer to the property manager
properties = Properties::get();
return 1;
}
/// method performing initialization of the GUI
int AppWorldLogic::initGUI()
{
// getting a GUI pointer
gui = Gui::get();
// creating a label widget and setting up its parameters
widget_label = WidgetLabel::create(gui, "Sun position:");
widget_label->setToolTip("Change sun position");
widget_label->setPosition(10, 10);
// creating a slider widget and setting up its parameters
widget_slider = WidgetSlider::create(gui, 0, 360, 90);
widget_slider->setToolTip("Change sun position");
widget_slider->setPosition(100, 10);
widget_slider->setCallback0(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));
// creating a checkbox widget and setting up its parameters
widget_checkbox = WidgetCheckBox::create(gui, "Automatic change");
widget_checkbox->setToolTip("Change sun position automatically");
widget_checkbox->setPosition(10, 30);
widget_checkbox->setChecked(1);
// creating a button widget and setting up its parameters
widget_button_del = WidgetButton::create(gui, "Delete");
widget_button_del->setToolTip("Delete object(s) selected in the combo box");
widget_button_del->setPosition(170, 50);
widget_button_del->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));
// creating a button widget and setting up its parameters
widget_button_fopen = WidgetButton::create(gui, "Load mesh");
widget_button_fopen->setToolTip("Load a mesh file");
widget_button_fopen->setPosition(10, 80);
widget_button_fopen->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonFopenClicked));
// creating a button widget and setting up its parameters
widget_button_close = WidgetButton::create(gui, "Quit");
widget_button_close->setToolTip("Close application");
widget_button_close->setPosition(10, 110);
widget_button_close->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonCloseClicked));
// creating a button widget and setting up its parameters
widget_button_wload = WidgetButton::create(gui, "Load world");
widget_button_wload->setToolTip("Load a default world");
widget_button_wload->setPosition(110, 110);
widget_button_wload->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonWloadClicked));
// creating a combo box widget and setting up its parameters
widget_combo = WidgetComboBox::create(gui);
widget_combo->addItem("----ALL-----");
widget_combo->setPosition(10, 50);
// creating a File Selection dialog widget and setting up its parameters
widget_file_dialog = WidgetDialogFile::create(gui, "Select a mesh file");
widget_file_dialog->setPath("../");
widget_file_dialog->setHidden(1);
widget_file_dialog->setFilter(".mesh");
widget_file_dialog->getCancelButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgCancelClicked));
widget_file_dialog->getOkButton()->setCallback0(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onDlgOKClicked));
// creating a Mesh Parameters dialog from an external ui-file
ui = UserInterface::create(gui, "user_interface.ui");
ui->setCallback0("button_OK", Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onMeshParamsOKClicked));
mesh_parameters_window = ui->getWidget(ui->findWidget("window"));
mesh_parameters_window->setHidden(1);
// adding created widgets to the UI
gui->addChild(widget_label->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 0 - label
gui->addChild(widget_slider->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 1 - slider
gui->addChild(widget_checkbox->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 2 - checkbox
gui->addChild(widget_button_del->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 3 - delete button
gui->addChild(widget_button_close->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 4 - close button
gui->addChild(widget_combo->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 5 - combo box
gui->addChild(widget_button_fopen->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 6 - Load Mesh button
gui->addChild(widget_button_wload->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED); // ID: 7 - Load world button
gui->addChild(mesh_parameters_window, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER); // ID: 8 - Mesh Parameters window
gui->addChild(widget_file_dialog->getWidget(), Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED | Gui::ALIGN_CENTER); // ID: 9 - File Open dialog
//reporting progress to the console
for (int k = 0; k < gui->getNumChildren(); k++)
{
Log::message("-> Adding UI element %d: %s.\n", k, gui->getChild(k)->getTypeName());
}
Log::warning("UI initialized! Number of elements: %d.\n", gui->getNumChildren());
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------- UPDATE METHODS ---------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------
/// method updating the position of the sun
int AppWorldLogic::updateLights()
{
// updating the Sun rotation angle
if (widget_checkbox->isChecked()){
sun_angle += SUN_ROTATION_RATE * ifps;
if (sun_angle > 360.0f) sun_angle = 0.0f;
// updating slider position
widget_slider->setValue(sun_angle);
}
// changing the Sun position using the new angle
LightWorld::cast(editor->getNodeByName("Sun"))->setWorldRotation(Math::quat(Math::vec3(0.0f, 1.0f, 0.0f), 180.0f - sun_angle));
return 1;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method updating the initial set of scene objects
int AppWorldLogic::updateObjects()
{
ObjectMeshDynamicPtr object;
// changing transformation for all scene objects named "my_meshdynamic_*" (initial set)
for (auto it = Objects.begin(); it != Objects.end(); it++)
{
object = it.get();
// getting the property assigned to the first surface of the object
PropertyPtr p = object->getProperty(0);
// checking if such property exists and has a "selected" parameter
if (p)
{
int param = p->findParameter("selected");
if (param != -1)
if (p->getParameterInt(param) == 1)
{
// if "selected" parameter value is 1 assigning "mesh_base" material to the object to highlight it
object->setMaterial("mesh_base", "*");
}
else
{
// if "selected" parameter value is 0 assigning the material that is stored in the "material" parameter of its property
object->setMaterial(p->getParameterString(p->findParameter("material")), "*");
}
}
// if a node is named "my_meshdynamic_*" (it belongs to the initial set) change its transformation
if (strstr(object->getName(), "my_meshdynamic_"))
{
// transform the node
transformNode(object->getNode(), ifps);
}
}
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;
}
//-----------------------------------------------------------------------------------------------------------------------------
/// method removing all created objects
int AppWorldLogic::removeObjects()
{
while (Objects.size() > 0)
{
removeMeshFromScene(Objects.begin()->get()->getName());
}
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 application
app = App::get();
// initializing pseudo-random number generator
Game::get()->setSeed(time(NULL));
// getting a pointer to the Editor
editor = Editor::get();
// creating materials
initMaterials();
// creating properties
initProperties();
// creating GUI
initGUI();
// 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.
// getting an inverse FPS value (the time in seconds it took to complete the last frame)
ifps = Game::get()->getIFps();
// checking if it's time to change current scale and forward vector direction of our objects
if (elapsed_time < 0.0f)
{
// change current scaling vector for objects
current_objects_scale = Math::vec3(Game::get()->getRandomFloat(0.8f, 1.2f));
// change forward direction for objects
forward_direction = forward_direction * Math::rotateZ(60.0f);
// resetting elapsed time counter
elapsed_time = CHANGE_INTERVAL;
}
// decreasing the time counter to the next change of current scale and forward vector direction
elapsed_time -= ifps;
// if right mouse button is clicked
if (app->getMouseButtonState(App::BUTTON_RIGHT))
{
// get and select object under the mouse cursor
selectObject(getObjectUnderMouseCursor(Game::get()->getPlayer(), app->getMouseX(), app->getMouseY(), 100.0f), old_selection);
}
// closing the application if a 'Q' key is pressed, ignoring the key if the console is opened
if (app->getKeyState('q') && !Console::get()->getActivity())
{
app->exit();
}
// updating objects
updateObjects();
// updating lights
updateLights();
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
removeObjects();
// clearing the player pointer
player.clear();
// clearing light sources
thesun.clear();
light_omni.clear();
projector.clear();
// clearing all created materials
clearMaterials();
// clearing gui elements
gui.clear();
// resetting old_selection pointer
old_selection.clear();
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;
}
Congratulations!
Well, now our project is ready! You have done a great job and learned how to use basic functions of the Unigine Engine. If you need more detailed information on how to use Unigine C++ API please refer to our extended API manual.