T3: Overview of Gadget Features¶
We have already mentioned the HIRO_DRAW library in the T2: Creating Custom Modules. Lets now look at more possibilities to render your data beyond simple geometries.
Important
Whenever you change the visualization without user interaction, method Gadget::DrawOnNextUpdate should be called to notify the system about the change.
The method is required when you are using the Optimized rendering setting,
otherwise, the system might not notice the change and the screen will not be redrawn.
Camera Focus¶
Have you seen and tryed the button with magnifying glass icon in the Gadget GUI titlebar?
That is the camer focus feature.
It allows users to immediately set camera pose, so that it views the data in scene correctly.
Override the method Gadget::FocusCamera in order to define the custom pose of camera.
GUI Generator¶
Another functionality that HIRO offers for custom gadgets, is a simple creation of an interactive GUI. One of the possible uses is manipulation with visual settings of rendered objects.
First of all, the function Gadget::GenerateGui must be overridden for your derived gadget class.
The method is called automatically by the system and gui parameter is a reference to GuiGenerator object that corresponds to this currently this gadget.
Options in the sidebar panel can be modified using this reference.
Explore possibilities of each GUI element in namespace hiro::gui documentation and use them to your advantage.
1 2 3 4 5 6 7 8 9 10 | void MyGadget::GenerateGui(hiro::GuiGenerator &gui)
{
hiro::Gadget::GenerateGui(gui);
gui.AddCheckbox("Material visible") // creates a checkbox with specified caption
->SetLink(&is_mat_visible_) // makes the variable to reflect the checkbox state
->Set(true) // changes the state of checkbox to true
->Subscribe([](const hiro::gui::Checkbox *checkbox) {
// this lambda function will be invoked every time a checkbox state is changed
});
};
|
It is possible to set up an element so that it will be visible only under some conditions.
This can be done using SetConditionFunc or SetConditionBool methods.
1 2 3 4 5 6 7 8 9 10 11 12 | ...
// Class MyGadget can has a boolean member variable called "is_mat_visible_".
// There are two ways how to set element to be visible only when the value is true.
gui.AddNumericInt("Material id 1")
->SetConditionBool(&is_mat_visible_);
gui.AddNumericInt("Material id 2")
->SetConditionFunc([this](){
return is_mat_visible_;
});
...
|
Method GenerateGui is called automatically, and cannot be called multiple times.
If you wish to re-generate your GUI, use method Resource::ResetGadgets - it refreshes all gadgets created by the resource that called this method.
Hint
During the creation of a gadget object, the following functions are called in this order:
Constructor
InitializeGenerateGui
When overriding method Initialize, be sure to also call superclass method Gadget::Initialize at the beginning.
Otherwise, some features of your Gadget will not work properly.
Text Printing¶
HIRO provides a very simple interface enabling to render any 2D text in the application window.
The Gadget class has a RenderTexts method.
You can simply override it and feed your own text to be rendered every frame.
1 2 3 4 5 6 7 8 9 10 11 12 | ...
void MyGadget::RenderTexts(hiro::draw::TextRenderer &t_renderer)
{
hiro::Gadget::RenderTexts(t_renderer);
t_renderer.SetAlignment(hiro::draw::TextAlignment::center);
t_renderer.SetColor(cogs::color::RED);
const auto proj = GetProjectionParams();
t_renderer.Print( {proj.width / 2, proj.height / 2}, "Your text here!");
DrawOnNextUpdate();
}
...
|
The method RenderTexts is automatically called by the engine on every draw, passing in a TextRenderer reference.
When overriding a method, make sure to call the parent’s method as well.
You can set various attributes, such as text alignment and color.
Finally, call the Print method providing a 2D position vector and your text.
Method TextRenderer::Print takes text position as the first parameter.
The position is relative to the top left corner of the viewarea in which the gadget exists.
Hint
Method GetProjectionParams returns ProjectionParams structure that describes the projection used in current viewarea. It holds several useful information about viewport, camera view, and projection and can be used for example to cast camera raya via ProjectionParams::CastRay.
Events¶
You may find yourself in a situation, when you wish to affect the renderers and their styles by the events produced by user. Gadget class provides several options for processing input such as:
keyboard key press/release
mouse button press/release
mouse wheel rotate
mouse move
view resize
Override theese methods to define your custom behavior.
This example shows processing keyboard key press event by overriding the method Gadget::KeyPressed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ...
hiro::EventStatus MyGadget::KeyPressed (
hiro::Key key,
const hiro::ModKeys &mods,
hiro::EventStatus status
)
{
if (hiro::Gadget::KeyPressed(key, mods, status) == hiro::EventStatus::processed)
{
return hiro::EventStatus::processed;
}
if (status == hiro::EventStatus::processed)
{
return hiro::EventStatus::unprocessed;
}
if (key == hiro::Key::space)
{
std::cout << "Space key was pressed!" << std::endl;
return hiro::EventStatus::processed;
}
return hiro::EventStatus::unprocessed;
}
...
|
The KeyPressed method comes with various parameters.
The parameter key simply describes which key was pressed by the user.
You can use the const hiro::ModKeys &mods parameter to check if any modifier keys, such as CTRL or Shift, were pressed.
Finally, the status is a state flag, specifying whether this event was already processed.
The good manners are, you should not process the event when it has status hiro::EventStatus::processed
since the event was already processed by another object, however, there are exceptions.
As before, always remember to call the parent’s method when overriding.
Hint
The event functions should return hiro::EventStatus::processed, if the event was processed in the function successfully.
This ensures the correct status within the engine.
Auto-save System¶
When debugging an algorithm, changes in the code may be introduced frequently and the application is started over and over. Also during application runtime, users can adjust rendering, show/hide some elements or change parameter values. To spare debugging time, it is recommended to store the state of gadget, that can be restored after the next application startup. Lucky you! HIRO offers a save/load system that is very simple to use.
If you wish to use an auto-save feature, call Gadget::LoadState in your Initialization method. This does two things.
First, it immediately loads the previously-stored state file and invokes ReadFromStream method.
Second, it tells the system that you wish to use the auto-save feature and from now on, WriteToStream will be triggered whenever a change to the generated GUI is introduced.
To define custom state values that should be stored/loaded, override ReadFromStream and WriteToStream methods.
Be sure to check the superclass load method as shown in the example below.
The boolean result of the method tells the system whether the data has been loaded correctly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ...
void MyGadget::Initialize()
{
Gadget::Initialize();
LoadState();
}
uint32_t MyGadget::STATE_VERSION = 0;
bool MyGadget::ReadFromStream(std::istream &str)
{
// Read super class state. If it fails, do not continue.
if (!Gadget::ReadFromStream(str))
return false;
// Read style. If it fails, do not continue.
if (!style_->ReadFromStream(str))
return false;
// Check if the file data is not deprecated. If it is, do not continue.
if (!ReadStateVersion(str, STATE_VERSION))
return false;
// Read custom properties.
std_ext::Read(is_mat_visible_, str);
// Everything went successfuly.
return true;
}
void MyGadget::WriteToStream(std::ostream &str)
{
// This method should reflect reading, except... it is writing.
Gadget::WriteToStream(str);
style_->WriteToStream(str);
WriteStateVersion(str, STATE_VERSION);
std_ext::Write(is_mat_visible_, str);
}
...
|
Constant STATE_VERSION is a custom number that defines the version of your read/write code.
It is recommended to use ReadStateVersion and WriteStateVersion to ensure that the values you read will not be corrupted when you read from file that is stale.
You should increase the STATE_VERSION number every time the changes are introduced to the read/write methods.
Attention
When changes were introduced to the ReadFromStream/WriteToStream method a common issue with state saving can be caused by the following:
changes were not reflected correctly in the other of the two methods, causing the inconsistency between the read and write code
STATE_VERSIONwas not increased, causing the stale old files to be considered new
Therefore, always take care when changing ReadFromStream/WriteToStream methods.