b3view/UserInterface.cpp

1481 lines
58 KiB
C++
Raw Normal View History

#include "Debug.h"
#include "Engine.h"
#include "Utility.h"
2010-04-21 07:48:36 -07:00
#include "UserInterface.h"
// class View : public irr::IEventReceiver; // avoid incomplete type when accessing member of this type from friend class
#include "View.h" // avoid incomplete type when accessing member of this type from friend class
#include <algorithm>
#include <iostream>
#include <string>
2019-03-09 09:51:25 -08:00
// NOTE: to use filesystem, you must also include the fs library such
// as via the `-lstdc++fs` linker option -- see b3view.pro
// #include <filesystem> // requires C++17
#include <experimental/filesystem> // requires C++14 such as gcc 8.2.1
2010-04-21 07:48:36 -07:00
using namespace irr;
using namespace irr::core;
using namespace irr::gui;
using std::string;
using std::wstring;
using namespace std;
// C++14: namespace filesystem = std::experimental::filesystem;
// namespace fs = std::filesystem; // doesn't work (not a namespace in gcc's C++17)
// using namespace std::filesystem; // doesn't work (not a namespace in gcc's C++17)
namespace fs = std::experimental::filesystem;
// namespace fs = std::filesystem; // doesn't work (not a namespace in gcc's C++17)
const u32 UserInterface::UIC_FILE_RECENT_FIRST = UIE_RECENTMENU + 1;
2010-04-21 07:48:36 -07:00
// PRIVATE
void UserInterface::setupUserInterface()
{
this->recent_initialized = false;
this->recentMenu = nullptr;
bool enableVerbose = false;
if (this->m_Engine != nullptr) {
enableVerbose = this->m_Engine->m_EnableVerbose;
} else {
std::cerr << "Error: The engine is not ready in setupUserInterface." << std::endl;
}
2019-03-07 10:23:54 -08:00
// Menu
menu = m_Gui->addMenu();
this->fileMenuIdx = menu->addItem(L"File", UIE_FILEMENU, true, true);
this->playbackMenuIdx = menu->addItem(L"Playback", UIE_PLAYBACKMENU, true, true);
this->viewMenuIdx = menu->addItem(L"View", UIE_VIEWMENU, true, true);
2010-04-21 07:48:36 -07:00
2019-03-07 10:23:54 -08:00
// File Menu
fileMenu = menu->getSubMenu(this->fileMenuIdx);
2019-04-19 12:29:30 -07:00
fileMenu->addItem(L"Open", UIC_FILE_OPEN);
2021-02-18 06:31:03 -08:00
this->fileRecentIdx = fileMenu->addItem(L"Open Recent", UIC_FILE_RECENT, true, true);
std::vector<std::string> recentPaths = this->m_Engine->recentPaths();
fileMenu->addItem(L"Reload Model F5", UIC_FILE_RELOAD_MESH);
fileMenu->addItem(L"Reload Texture Shift F5", UIC_FILE_RELOAD_TEXTURE);
2019-04-19 12:29:30 -07:00
fileMenu->addItem(L"Change Texture", UIC_FILE_OPEN_TEXTURE);
2019-04-19 12:50:06 -07:00
fileMenu->addItem(L"Previous Texture Shift F3", UIC_FILE_PREVIOUS_TEXTURE);
fileMenu->addItem(L"Next Texture F3", UIC_FILE_NEXT_TEXTURE);
2019-05-16 09:33:53 -07:00
fileMenu->addItem(L"Export DAE (non-Blender COLLADA)", UIC_FILE_EXPORT_DAE);
2019-05-17 05:07:39 -07:00
fileMenu->addItem(L"Export IRR (Irrlicht Scene settings and mesh paths only)", UIC_FILE_EXPORT_IRR);
2019-05-16 09:33:53 -07:00
fileMenu->addItem(L"Export IRRMESH (Static Irrlicht Mesh)", UIC_FILE_EXPORT_IRRMESH);
fileMenu->addItem(L"Export OBJ (Wavefront)", UIC_FILE_EXPORT_OBJ);
fileMenu->addItem(L"Export STL (stereolithography)", UIC_FILE_EXPORT_STL);
fileMenu->addItem(L"Quit", UIC_FILE_QUIT);
2010-04-21 07:48:36 -07:00
2021-02-18 06:31:03 -08:00
// File, Open Recent submenu
this->recentMenu = fileMenu->getSubMenu(this->fileRecentIdx);
if (enableVerbose) {
std::cerr << "+this->recentMenu text:\"" << Utility::toString((wstring)this->recentMenu->getText()) << "\""
<< " idx:" << Utility::toString((int)this->fileRecentIdx)
<< " id:" << Utility::toString((int)this->recentMenu->getID())
<< std::endl;
}
this->fileRecentClearIdx = this->recentMenu->addItem(L"Clear Recent", UIC_FILE_RECENT_CLEAR);
if (enableVerbose) {
std::cerr << "+this->fileRecentClearIdx: " << this->fileRecentClearIdx << std::endl;
}
this->uic_file_recent_next = UserInterface::UIC_FILE_RECENT_FIRST;
2021-02-18 06:31:03 -08:00
this->m_file_recent_first_idx = -1;
this->m_file_recent_last_idx = -1;
this->recent_initialized = true;
this->addRecentMenuItems(recentPaths, false);
2021-02-18 06:31:03 -08:00
// Playback Menu
playbackMenu = menu->getSubMenu(this->playbackMenuIdx);
playbackMenu->addItem(L"Previous Frame Left",
UIC_PLAYBACK_PREVIOUS, true, false,
false, false);
playbackMenu->addItem(L"Next Frame Right",
UIC_PLAYBACK_NEXT, true, false,
false, false);
playbackMenu->addItem(L"Slower Ctrl Left",
UIC_PLAYBACK_SLOWER, true, false,
false, false);
playbackMenu->addItem(L"Faster Ctrl Right",
UIC_PLAYBACK_FASTER, true, false,
false, false);
2010-08-16 05:23:20 -07:00
// View Menu
viewMenu = menu->getSubMenu(this->viewMenuIdx);
2019-04-19 14:13:58 -07:00
viewWireframeIdx = viewMenu->addItem(L"Wireframe",
UIC_VIEW_WIREFRAME, true,
false, this->m_Engine->getEnableWireframe(), true);
2019-04-19 14:13:58 -07:00
viewLightingIdx = viewMenu->addItem(L"Lighting",
UIC_VIEW_LIGHTING, true,
false, this->m_Engine->getEnableLighting(), true);
2019-04-19 14:13:58 -07:00
viewAxisWidgetIdx = viewMenu->addItem(L"Origin Axis Widget",
UIC_VIEW_AXIS_WIDGET, true, false,
true, true);
viewTargetIdx = viewMenu->addItem(L"Camera Target",
UIC_VIEW_TARGET, true, false,
false, true);
viewTextureInterpolationIdx = viewMenu->addItem(L"Texture Interpolation Ctrl i",
UIC_VIEW_TEXTURE_INTERPOLATION, true, false,
this->m_Engine->getEnableTextureInterpolation(), true);
2019-04-19 14:13:58 -07:00
viewYUpIdx = viewMenu->addItem(L"Y Up",
UIC_VIEW_Y_UP, true, false,
true, true);
viewZUpIdx = viewMenu->addItem(L"Z Up",
UIC_VIEW_Z_UP, true, false,
false, true);
2010-08-16 05:23:20 -07:00
2019-03-07 21:52:29 -08:00
// Playback Control Window
dimension2d<u32> windowSize = m_Engine->m_Driver->getScreenSize();
playbackWindow = m_Gui->addWindow(
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(windowSize.Width - 4 - 160, 28),
dimension2d<s32>(160, 500)),
2019-04-19 14:13:58 -07:00
false,
L"Playback",
nullptr,
UIE_PLAYBACKWINDOW
);
playbackWindow->getCloseButton()->setVisible(false);
2019-03-07 21:52:29 -08:00
s32 spacing_x = 4;
2019-04-19 12:29:30 -07:00
s32 margin_y = 4;
spacing_y = 4;
2019-03-07 21:52:29 -08:00
s32 size_x = playbackWindow->getClientRect().getWidth() - 8;
s32 size_y = 24;
s32 y = 24;
2020-07-30 20:40:17 -07:00
playbackStartFrameStaticText = m_Gui->addStaticText(
L"Start Frame:",
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
true,
playbackWindow,
UIE_PLAYBACKSTARTFRAMESTATICTEXT,
false);
y += size_y;
playbackStartFrameEditBox = m_Gui->addEditBox(
L"",
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
playbackWindow,
UIE_PLAYBACKSTARTFRAMEEDITBOX);
y += size_y + spacing_y;
playbackEndFrameStaticText = m_Gui->addStaticText(
L"End Frame:",
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
true,
playbackWindow,
UIE_PLAYBACKENDFRAMESTATICTEXT,
false);
y += size_y;
playbackEndFrameEditBox = m_Gui->addEditBox(
L"",
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
playbackWindow,
UIE_PLAYBACKENDFRAMEEDITBOX);
y += size_y + spacing_y;
2019-03-07 21:52:29 -08:00
playbackStartStopButton = m_Gui->addButton(
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
playbackWindow,
UIE_PLAYBACKSTARTSTOPBUTTON,
L"Start/Stop",
nullptr);
2019-04-19 12:29:30 -07:00
y += size_y + spacing_y;
playbackSetFrameEditBox = m_Gui->addEditBox(
L"",
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
2019-04-19 12:29:30 -07:00
true,
playbackWindow,
UIE_PLAYBACKSETFRAMEEDITBOX);
y += margin_y;
2019-03-07 21:52:29 -08:00
y += size_y + spacing_y;
playbackIncreaseButton = m_Gui->addButton(
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
playbackWindow,
UIE_PLAYBACKINCREASEBUTTON,
L"Faster",
nullptr);
2019-04-19 12:29:30 -07:00
2019-03-07 21:52:29 -08:00
y += size_y + spacing_y;
playbackDecreaseButton = m_Gui->addButton(
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
playbackWindow,
UIE_PLAYBACKDECREASEBUTTON,
L"Slower",
nullptr);
y += size_y + spacing_y;
2019-04-19 12:29:30 -07:00
playbackFPSEditBox = m_Gui->addEditBox(
L"",
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
playbackWindow,
2019-04-19 12:29:30 -07:00
UIE_FPSEDITBOX);
y += margin_y;
y += size_y + spacing_y;
texturePathStaticText = m_Gui->addStaticText(
L"Texture Path:",
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
true,
playbackWindow,
UIE_TEXTUREPATHSTATICTEXT,
false);
2019-04-19 12:29:30 -07:00
y += size_y + spacing_y;
texturePathEditBox = m_Gui->addEditBox(
L"",
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
true,
playbackWindow,
UIE_TEXTUREPATHEDITBOX);
texturePathEditBox ->setTextAlignment(EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT);
2019-04-19 12:29:30 -07:00
y += margin_y;
y += size_y + spacing_y;
axisSizeStaticText = m_Gui->addStaticText(
L"Axis Size:",
2019-04-19 14:34:31 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
2019-04-19 14:13:58 -07:00
dimension2d<s32>(size_x, size_y)),
2019-04-19 12:29:30 -07:00
true,
true,
playbackWindow,
UIE_AXISSIZESTATICTEXT,
false);
y += size_y + spacing_y;
axisSizeEditBox = m_Gui->addEditBox(
std::to_wstring(this->m_Engine->m_AxisLength).c_str(),
2019-04-19 14:13:58 -07:00
rect<s32>(vector2d<s32>(spacing_x, y),
dimension2d<s32>(size_x, size_y)),
2019-04-19 12:29:30 -07:00
true,
playbackWindow,
UIE_AXISSIZEEDITBOX);
y += margin_y;
y += size_y + spacing_y;
2010-04-21 07:48:36 -07:00
// Set Font for UI Elements
m_GuiFontFace = new CGUITTFace();
// irrString defines stringc as string<c8>
2019-03-09 13:41:56 -08:00
if (!Utility::isFile(m_Engine->m_FontPath)) {
m_Engine->m_FontPath = L"C:\\Windows\\Fonts\\calibrib.ttf";
}
if (!Utility::isFile(m_Engine->m_FontPath)) {
m_Engine->m_FontPath = L"C:\\Windows\\Fonts\\arialbd.ttf";
}
if (!Utility::isFile(m_Engine->m_FontPath)) {
2019-03-09 14:15:50 -08:00
m_Engine->m_FontPath = L"/usr/share/fonts/liberation/LiberationSans-Bold.ttf";
2019-03-09 13:41:56 -08:00
}
if (!Utility::isFile(m_Engine->m_FontPath)) {
2019-03-09 14:15:50 -08:00
m_Engine->m_FontPath = L"/usr/share/fonts/gnu-free/FreeSansBold.ttf";
2019-03-09 13:41:56 -08:00
}
if (!Utility::isFile(m_Engine->m_FontPath)) {
m_Engine->m_FontPath = L"/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf";
}
if (!Utility::isFile(m_Engine->m_FontPath)) {
m_Engine->m_FontPath = L"/usr/share/fonts/google-droid/DroidSans-Bold.ttf";
}
2021-03-28 03:56:19 -07:00
if (!Utility::isFile(m_Engine->m_FontPath)) {
m_Engine->m_FontPath = L"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf";
// ^ This is present on Debian 10. No previous fonts further up are.
}
// if (!Utility::isFile(m_Engine->m_FontPath)) {
// m_Engine->m_FontPath = L"/usr/share/fonts-droid-fallback/truetype/DroidSansFallback.ttf";
// /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf
// ^ These are CJK fonts!
// }
2019-03-09 13:41:56 -08:00
if (m_GuiFontFace->load(m_Engine->m_FontPath.c_str())) { // actually takes `const io::path &`
m_GuiFont = new CGUITTFont(m_Gui);
m_GuiFont->attach(m_GuiFontFace, 14);
m_Gui->getSkin()->setFont(m_GuiFont);
} else {
if (enableVerbose) {
std::wcerr << L"WARNING: '" << m_Engine->m_FontPath << L"' is missing."
<< endl;
}
delete m_GuiFontFace;
m_GuiFontFace = nullptr;
if (m_GuiFont != nullptr) {
if (enableVerbose) {
std::wcerr << L" - The old font will remain loaded." << endl;
}
}
2019-03-07 10:23:54 -08:00
}
2019-04-19 14:16:28 -07:00
// }
2010-04-21 07:48:36 -07:00
}
void UserInterface::displayLoadFileDialog()
{
2021-03-28 04:05:15 -07:00
m_Gui->addFileOpenDialog(L"Select a file to load",
2019-04-19 14:13:58 -07:00
true, nullptr, UIE_LOADFILEDIALOG);
2010-04-21 07:48:36 -07:00
}
2019-05-02 18:17:24 -07:00
void UserInterface::displaySaveFileDialog()
{
m_Gui->addFileOpenDialog(L"Select where to save export.dae",
true, nullptr, UIE_SAVEFILEDIALOG);
// NOTE: if restoreCWD is false (default), cwd changes.
}
2019-03-07 14:17:42 -08:00
void UserInterface::displayLoadTextureDialog()
{
2021-03-28 04:05:15 -07:00
m_Gui->addFileOpenDialog(L"Select a file to load",
2019-04-19 14:13:58 -07:00
true, nullptr, UIE_LOADTEXTUREDIALOG);
2019-03-07 14:17:42 -08:00
}
void UserInterface::incrementFrame(f32 frameCount, bool enableRound)
{
if (this->m_Engine->m_LoadedMesh != nullptr) {
if (this->m_Engine->m_IsPlaying)
this->m_Engine->toggleAnimation();
this->m_Engine->m_LoadedMesh->setCurrentFrame(
enableRound
? (round(this->m_Engine->m_LoadedMesh->getFrameNr()) + frameCount)
: (round(this->m_Engine->m_LoadedMesh->getFrameNr()) + frameCount)
);
this->playbackSetFrameEditBox->setText(
Utility::toWstring(
this->m_Engine->m_LoadedMesh->getFrameNr()
).c_str()
);
}
}
bool UserInterface::handleMenuItemPressed(const SEvent::SGUIEvent* ge)
{
// ^ formerly ...(IGUIContextMenu* menu): called as ...(static_cast<IGUIContextMenu*>(ge->Caller)); protytyped as ...(irr::gui::IGUIContextMenu* menu)
bool handled = true;
IGUIContextMenu* menu = static_cast<IGUIContextMenu*>(ge->Caller);
s32 callerID = ge->Caller->getID();
s32 selected = menu->getSelectedItem();
s32 commandID = menu->getItemCommandId(static_cast<u32>(selected));
bool enableVerbose = this->m_Engine->m_EnableVerbose;
switch (callerID) {
case UIE_RECENTMENU:
// if ((ge->Caller->getID() >= this->m_file_recent_first_idx)
// && (ge->Caller->getID() <= m_file_recent_last_idx)) {
// NOTE: ge->Caller->getID() is probably UIE_RECENTMENU now, but that is not to be used directly!
if (enableVerbose) {
cerr << "selected " << selected << std::endl;
}
if (std::find(this->recentIndices.begin(), this->recentIndices.end(), commandID) != this->recentIndices.end()) {
// ge->Caller->getText() // Don't do this. Caller is the parent!
if (enableVerbose) {
cerr << "parent callerID: " << callerID << endl;
// ^ callerID is the parent such as 1100 (or whatever UI_RECENTMENU is)
cerr << " commandID: " << commandID << std::endl;
// ^ commandID is a menu id specified on creation
// such as starting from 1101
// or from whatever UIC_FILE_RECENT_FIRST is--usually UI_RECENTMENU+1).
// selectedItemID is a sequential number.
}
// std::wstring menuItemText = menu->getItemText(selected);
this->openRecent(commandID, selected);
}
else {
cerr << "Unknown commandID: " << commandID << " Text:" << Utility::toString(menu->getItemText(selected)) << endl;
// ^ getItemText takes the index (NOT the commandID specified on creation)
if (this->recentIndices.size() < 1) {
cerr << "- recentIndices.size(): " << recentIndices.size() << endl;
}
else {
if (enableVerbose) {
cerr << " recentIndices: " << recentIndices.size() << endl;
// range based for loop requires C++11 or higher:
for(irr::u32 i : this->recentIndices) {
cerr << " - " << i << endl;
}
}
}
handled = false;
}
// cerr << "[UserInterface::handleMenuItemPressed] Unknown caller id: " << callerID << endl;
break;
default:
//if (selected > -1) {
cerr << "Some other menu was used: " << callerID
<< " Text:" << Utility::toString(menu->getItemText(selected))
<< std::endl;
cerr << " - checking command id..."
<< std::endl;
switch (commandID) {
2019-04-19 12:29:30 -07:00
case UIC_FILE_OPEN:
displayLoadFileDialog();
break;
2021-02-18 06:31:03 -08:00
case UIC_FILE_RECENT_CLEAR:
clearRecent();
break;
2019-05-16 09:33:53 -07:00
case UIC_FILE_EXPORT_DAE:
exportMeshToHome("dae");
break;
case UIC_FILE_EXPORT_IRR:
exportMeshToHome("irr");
break;
case UIC_FILE_EXPORT_IRRMESH:
exportMeshToHome("irrmesh");
break;
case UIC_FILE_EXPORT_OBJ:
exportMeshToHome("obj");
break;
case UIC_FILE_EXPORT_STL:
exportMeshToHome("stl");
2019-05-02 18:17:24 -07:00
break;
case UIC_FILE_RELOAD_MESH:
m_Engine->reloadMesh();
break;
case UIC_FILE_RELOAD_TEXTURE:
m_Engine->reloadTexture();
break;
2019-04-19 12:29:30 -07:00
case UIC_FILE_OPEN_TEXTURE:
2019-05-16 09:33:53 -07:00
if (m_Engine->m_LoadedMesh != nullptr) {
displayLoadTextureDialog();
}
else {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Change Texture", L"You must load a model before a texture.");
}
break;
2019-04-19 12:29:30 -07:00
case UIC_FILE_PREVIOUS_TEXTURE:
2019-05-16 09:33:53 -07:00
if (m_Engine->m_LoadedMesh != nullptr) {
loadNextTexture(-1);
}
else {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Change Texture", L"You must load a model before a texture.");
}
2019-04-19 12:29:30 -07:00
break;
case UIC_FILE_NEXT_TEXTURE:
2019-05-16 09:33:53 -07:00
if (m_Engine->m_LoadedMesh != nullptr) {
loadNextTexture(1);
}
else {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Change Texture", L"You must load a model before a texture.");
}
2019-04-19 12:29:30 -07:00
break;
case UIC_FILE_QUIT:
m_Engine->m_RunEngine = false;
break;
case UIC_PLAYBACK_PREVIOUS:
this->incrementFrame(-1.0f, true);
break;
case UIC_PLAYBACK_NEXT:
this->incrementFrame(1.0f, true);
break;
case UIC_PLAYBACK_SLOWER:
//if (ge->EventType == EGET_BUTTON_CLICKED) {
this->m_Engine->incrementAnimationFPS(-5);
//}
break;
case UIC_PLAYBACK_FASTER:
//if (ge->EventType == EGET_BUTTON_CLICKED) {
this->m_Engine->incrementAnimationFPS(5);
//}
break;
2019-04-19 12:29:30 -07:00
case UIC_VIEW_TARGET:
//
break;
case UIC_VIEW_Y_UP:
m_Engine->setZUp(false);
viewMenu->setItemChecked(viewZUpIdx, false);
break;
case UIC_VIEW_Z_UP:
m_Engine->setZUp(true);
viewMenu->setItemChecked(viewYUpIdx, false);
break;
case UIC_VIEW_WIREFRAME:
m_Engine->setEnableWireframe(
!m_Engine->getEnableWireframe()
);
viewMenu->setItemChecked(
viewWireframeIdx,
m_Engine->getEnableWireframe()
);
break;
case UIC_VIEW_LIGHTING:
m_Engine->setEnableLighting(
!m_Engine->getEnableLighting()
);
viewMenu->setItemChecked(
viewLightingIdx,
m_Engine->getEnableLighting()
);
break;
case UIC_VIEW_TEXTURE_INTERPOLATION:
m_Engine->setEnableTextureInterpolation(
!m_Engine->getEnableTextureInterpolation()
);
viewMenu->setItemChecked(
viewTextureInterpolationIdx,
m_Engine->getEnableTextureInterpolation()
2019-04-19 14:13:58 -07:00
);
break;
default:
cerr << "Unknown command id: " << commandID << " Text:" << Utility::toString(menu->getItemText(selected)) << endl;
break;
}
break;
}
return handled;
}
void UserInterface::updateSettingsDisplay()
{
viewMenu->setItemChecked(
viewWireframeIdx,
m_Engine->getEnableWireframe()
);
viewMenu->setItemChecked(
viewLightingIdx,
m_Engine->getEnableLighting()
);
viewMenu->setItemChecked(
viewTextureInterpolationIdx,
m_Engine->getEnableTextureInterpolation()
);
}
void UserInterface::snapWidgets()
{
dimension2d<u32> screenSize = m_Engine->m_Driver->getScreenSize();
rect<s32> newRect;
2019-04-19 14:16:28 -07:00
// newRect.LowerRightCorner.X = static_cast<s32>(size.Width);
// newRect.LowerRightCorner.Y = static_cast<s32>(size.Height);
rect<s32> prevRect = playbackWindow->getRelativePosition();
2019-04-19 14:13:58 -07:00
newRect.UpperLeftCorner.X = static_cast<s32>(screenSize.Width)
- prevRect.getWidth() - spacing_y;
// debug() << "screen size: " << screenSize.Width << "x" << screenSize.Height;
// debug() << " prevRect: "
// << prevRect.UpperLeftCorner.X << ","
// << prevRect.UpperLeftCorner.Y << ","
// << prevRect.LowerRightCorner.X << ","
// << prevRect.LowerRightCorner.Y
// << " size=(" << prevRect.getWidth() << ","
// << prevRect.getHeight() << ")" << endl;
newRect.UpperLeftCorner.Y = prevRect.UpperLeftCorner.Y;
2019-04-19 14:13:58 -07:00
newRect.LowerRightCorner.X = newRect.UpperLeftCorner.X
+ prevRect.getWidth();
newRect.LowerRightCorner.Y = newRect.UpperLeftCorner.Y
+ prevRect.getHeight();
playbackWindow->setRelativePosition(newRect);
m_WindowSize.Width = m_Engine->m_Driver->getScreenSize().Width;
m_WindowSize.Height = m_Engine->m_Driver->getScreenSize().Height;
}
2010-04-21 07:48:36 -07:00
// PUBLIC
UserInterface::UserInterface(Engine* engine)
2010-04-21 07:48:36 -07:00
{
this->recent_initialized = false;
2019-04-19 12:29:30 -07:00
viewTextureInterpolationIdx = 0;
viewWireframeIdx = 0;
viewLightingIdx = 0;
2019-03-07 21:52:29 -08:00
this->playbackStartStopButton = nullptr;
2010-04-21 07:48:36 -07:00
m_Engine = engine;
m_Gui = engine->getGUIEnvironment();
playbackWindow = nullptr;
2010-08-16 05:23:20 -07:00
2010-04-21 07:48:36 -07:00
setupUserInterface();
}
UserInterface::~UserInterface()
{
delete m_GuiFont;
delete m_GuiFontFace;
2010-04-21 07:48:36 -07:00
}
IGUIEnvironment* UserInterface::getGUIEnvironment() const
2010-04-21 07:48:36 -07:00
{
return m_Gui;
}
void UserInterface::drawStatusLine() const
2010-04-21 07:48:36 -07:00
{
}
bool UserInterface::OnSelectMesh() {
this->m_MatchingTextures.clear();
this->m_AllTextures.clear();
return true;
}
2020-07-30 20:40:17 -07:00
void UserInterface::setPlaybackText(s32 id, const wchar_t* text)
{
switch (id) {
case (UIE_PLAYBACKSTARTFRAMEEDITBOX):
this->playbackStartFrameEditBox->setText(text);
break;
case (UIE_PLAYBACKENDFRAMEEDITBOX):
this->playbackEndFrameEditBox->setText(text);
break;
default:
std::cerr << "ERROR: setPlaybackText got a bad id: " << id << std::endl;
// break;
}
}
/**
* Load the next texture from the list of found textures.
* Files are only listed once for speed, so you must reload the
* model to trigger another list ("dir") operation (since loading
* a mesh calls OnSelectMesh() which clears allTextures and matchingTextures).
*
* @param direction Specify <0 to choose previous texture, >0 for next, 0 to
* reload current texture if any; otherwise, only select a texture if any
* matching textures (named like model) are present in . or ../textures.
* @return Any texture was loaded (true/false).
*/
bool UserInterface::loadNextTexture(int direction)
{
bool enableVerbose = false;
if (this->m_Engine != nullptr) {
enableVerbose = this->m_Engine->m_EnableVerbose;
}
if (enableVerbose) {
cerr << "Loading texture..." << flush;
}
bool ret = false;
std::wstring basePath = L".";
if (this->m_Engine->m_LoadedMeshPath.length() > 0) {
2019-07-03 14:13:37 -07:00
std::wstring prevModelName = Utility::basename(
this->m_Engine->m_LoadedMeshPath
2019-04-19 14:13:58 -07:00
);
wstring foundPath;
2019-07-03 14:13:37 -07:00
wstring prevModelNoExt;
prevModelNoExt = Utility::withoutExtension(prevModelName);
/*
vector<wstring> names;
2019-07-03 14:13:37 -07:00
names.push_back(prevModelNoExt+L"_mesh");
names.push_back(prevModelNoExt);
names.push_back(prevModelNoExt+L"_1");
names.push_back(prevModelNoExt+L"_2");
names.push_back(prevModelNoExt+L"_1");
names.push_back(prevModelNoExt+L"_2");
names.push_back(prevModelNoExt+L"_01");
names.push_back(prevModelNoExt+L"_02");
names.push_back(prevModelNoExt+L"_01");
names.push_back(prevModelNoExt+L"_02");
names.push_back(prevModelNoExt+L"_child");
names.push_back(prevModelNoExt+L"_female");
names.push_back(prevModelNoExt+L"_f");
names.push_back(prevModelNoExt+L"_male");
names.push_back(prevModelNoExt+L"_m");
*/
2019-07-03 14:13:37 -07:00
vector<wstring> badSuffixes;
badSuffixes.push_back(L"_inv");
2019-04-19 14:13:58 -07:00
std::wstring lastDirPath = Utility::parentOfPath(
this->m_Engine->m_LoadedMeshPath
2019-04-19 14:13:58 -07:00
);
std::wstring parentPath = Utility::parentOfPath(lastDirPath);
2019-04-19 14:13:58 -07:00
std::wstring dirSeparator = Utility::delimiter(
this->m_Engine->m_LoadedMeshPath
2019-04-19 14:13:58 -07:00
);
std::wstring texturesPath = parentPath + dirSeparator + L"textures";
2019-07-03 14:13:37 -07:00
std::wstring tryTexPath = texturesPath + dirSeparator + prevModelNoExt
2019-04-19 14:13:58 -07:00
+ L".png";
vector<wstring> texturePaths;
2019-07-03 14:13:37 -07:00
texturePaths.push_back(lastDirPath);
if (fs::is_directory(fs::status(texturesPath))) {
texturePaths.push_back(texturesPath);
}
vector<wstring> dotExts;
for (auto ext : this->m_Engine->m_TextureExtensions) {
dotExts.push_back(L"." + ext);
}
if (this->m_MatchingTextures.size() + this->m_AllTextures.size() < 1) {
for (auto path : texturePaths) {
for (const auto& itr : fs::directory_iterator(path)) {
if (fs::is_regular_file(itr.status())) {
std::wstring name = itr.path().filename().wstring();
std::wstring suffix = Utility::getSuffix(name, dotExts,
true);
bool isUsable = true;
std::wstring nameNoExt = Utility::withoutExtension(
name
);
if (Utility::endsWithAny(nameNoExt, badSuffixes, true))
isUsable = false;
if (isUsable && suffix.length() > 0) {
this->m_AllTextures.push_back(
path + dirSeparator + name
);
if (Utility::startsWith(name, prevModelNoExt)) {
this->m_MatchingTextures.push_back(
path + dirSeparator + name
);
2019-07-03 14:13:37 -07:00
}
else if (name.find(prevModelNoExt) != std::wstring::npos) {
this->m_MatchingTextures.push_back(
path + dirSeparator + name
);
2019-07-03 14:13:37 -07:00
}
else if (name.find(Utility::replaceAll(prevModelNoExt, L"_", L"")) != std::wstring::npos) {
this->m_MatchingTextures.push_back(
path + dirSeparator + name
);
}
}
}
}
}
}
vector<wstring> paths = this->m_MatchingTextures;
if (this->m_MatchingTextures.size() < 1) {
paths = this->m_AllTextures;
if (enableVerbose) {
debug() << "There were no matching textures so"
<< " the entire list of " << this->m_AllTextures.size()
<< " found textures will be used..." << std::flush;
}
}
else {
// Assume the user wants to view name-matched texture using
// the render settings of Minetest.
this->m_Engine->setEnableTextureInterpolation(false);
viewMenu->setItemChecked(
viewTextureInterpolationIdx,
this->m_Engine->getEnableTextureInterpolation()
);
}
std::wstring prevTexture = L"";
std::wstring nextTexture = L"";
std::wstring lastTexture = L"";
std::wstring firstTexture = L"";
bool found = false;
for (auto path : paths) {
if (firstTexture.length() == 0)
firstTexture = path;
lastTexture = path;
if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
if (path == this->m_Engine->m_LoadedTexturePath) {
found = true;
}
else if (!found) {
prevTexture = path;
}
else {
if (nextTexture.length() == 0)
nextTexture = path;
}
}
else {
prevTexture = path; // Use the last one as the previous.
if (nextTexture.length() == 0)
nextTexture = path;
}
}
if (nextTexture.length() == 0)
nextTexture = firstTexture; // The last is current, so next is 1st.
if (prevTexture.length() == 0) {
if (lastTexture != firstTexture)
prevTexture = lastTexture; // Wrap to end.
else
prevTexture = firstTexture; // Use the only texture.
}
if (lastTexture.length() > 0) {
if (direction < 0) {
if (enableVerbose) {
cerr << "loading the previous texture...";
}
ret = this->m_Engine->loadTexture(prevTexture, false);
}
else if (direction > 0) {
if (enableVerbose) {
cerr << "loading the next texture...";
}
ret = this->m_Engine->loadTexture(nextTexture, false);
}
else {
// If direction is 0 (such as when a model is loaded that has
2021-03-28 03:08:53 -07:00
// no texture), only load a specified or matching texture.
if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
if (enableVerbose) {
cerr << "using a specified texture...";
}
ret = this->m_Engine->loadTexture(
this->m_Engine->m_LoadedTexturePath,
false
);
}
else if (this->m_MatchingTextures.size() >= 1) {
if (enableVerbose) {
cerr << "loading matching texture...";
}
ret = this->m_Engine->loadTexture(firstTexture, false);
}
2021-03-28 03:08:53 -07:00
else {
ret = true;
if (enableVerbose) {
cerr << "(cycling was off and there is no matching texture) ";
}
2021-03-28 03:08:53 -07:00
}
}
}
else if (this->m_Engine->m_LoadedTexturePath.length() > 0) {
if (enableVerbose) {
cerr << "loading the first texture...";
}
ret = this->m_Engine->loadTexture(
this->m_Engine->m_LoadedTexturePath,
false
);
}
} else {
if (enableVerbose) {
debug() << "Can't cycle texture since no file was opened" << endl;
}
}
if (enableVerbose) {
cerr << (ret?"OK":"FAILED") << endl;
}
return ret;
}
2019-05-16 09:33:53 -07:00
void UserInterface::exportMeshToHome(std::string extension)
{
if (this->m_Engine->m_LoadedMesh != nullptr) {
// this->m_Engine->m_LoadedMesh->getName();
// displaySaveFileDialog();
irr::io::path where = irr::io::path();
if (const char* env_p = std::getenv("HOME")) {
// std::cout << "Your PATH is: " << env_p << '\n';
where = irr::io::path(env_p);
std::cout << "Your PATH is: " << where.c_str() << '\n';
}
else if (const char* env_p = std::getenv("USERPROFILE")) {
// std::cout << "Your PATH is: " << env_p << '\n';
where = irr::io::path(env_p);
std::cout << "Your PATH is: " << where.c_str() << '\n';
}
std::string name = "";
if (m_Engine->m_LoadedMeshPath.length() > 0) {
name = Utility::toString(Utility::withoutExtension(Utility::basename(m_Engine->m_LoadedMeshPath)));
2019-05-16 09:33:53 -07:00
}
2019-05-16 09:33:53 -07:00
wstring result = m_Engine->saveMesh(where, name, extension);
std::wstring caption = L"Export Failed";
std::wstring msg = L"The format or home variable is unwriteable";
if (result.length() > 0) {
caption = L"Export Finished";
msg = L"Saved " + result;
}
std::cout << "Exported as: " << Utility::toString(result) << '\n';
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
caption.c_str(), msg.c_str());
}
else {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Export", L"There is nothing to export.");
}
}
2021-02-18 06:31:03 -08:00
void UserInterface::clearRecent()
{
// for (int idx=this->uic_file_recent_next-1; idx>=UserInterface::uic_file_recent_first; idx--) {
2021-02-18 06:31:03 -08:00
for (std::vector<u32>::iterator idxIt = this->recentIndices.begin(); idxIt != this->recentIndices.end(); ++idxIt) {
this->recentMenu->removeItem(*idxIt);
}
this->recentIndices.clear();
this->uic_file_recent_next = UserInterface::UIC_FILE_RECENT_FIRST;
2021-02-18 06:31:03 -08:00
this->m_file_recent_first_idx = -1;
this->m_file_recent_last_idx = -1;
}
void UserInterface::addRecentMenuItem(std::string path, bool addToEngine)
2021-02-18 06:31:03 -08:00
{
bool enableVerbose = false;
if (this->m_Engine != nullptr) {
enableVerbose = this->m_Engine->m_EnableVerbose;
} else {
std::cerr << "Error: m_Engine isn't ready in addRecentMenuItem." << std::endl;
}
if (!this->recent_initialized) {
2021-03-28 03:56:19 -07:00
throw std::runtime_error("The UI is not ready in addRecentMenuItem.");
}
if (enableVerbose) {
std::cerr << "[addRecentMenuItem] " << path << "..." << std::endl;
}
2021-02-18 06:31:03 -08:00
if (!this->hasRecent(path)) {
if (enableVerbose) {
std::cerr << "* adding since new..." << std::endl;
}
wstring path_ws = Utility::toWstring(path);
if (this->uic_file_recent_next < UserInterface::UIC_FILE_RECENT_FIRST) {
throw std::runtime_error("this->uic_file_recent_next is "
+ std::to_string(this->uic_file_recent_next)
+ " but should be equal to or greater than first: "
+ std::to_string(this->uic_file_recent_next));
}
// The first this->uic_file_recent_next is 1101 or whatever
// UserInterface::UIC_FILE_RECENT_FIRST (usually UIC_FILE_RECENT+1) is.
u32 newI = this->recentMenu->addItem(path_ws.c_str(), this->uic_file_recent_next);
if (enableVerbose) {
std::cerr << "+this->recentMenu->addItem"
<< " idx:" << newI
<< " commandID:" << this->uic_file_recent_next
<< std::endl;
}
// IGUIContextMenu* menu = this->recentMenu->getSubMenu(newI);
// NOTE: Caller would be the parent menu id on click!
// newI is a sequential number starting at 1 which becomes the
// selected item (See menu->getSelectedItem() in handleMenuItemPressed)
// this->recentIndices.push_back(newI);
this->recentIndices.push_back(this->uic_file_recent_next);
this->uic_file_recent_next++;
2021-02-18 06:31:03 -08:00
/*
if (this->m_file_recent_first_idx < 0) {
this->m_file_recent_first_idx = menu->getID(); // SIGSEGV crash
}
this->m_file_recent_last_idx = menu->getID();
*/
if (addToEngine) {
this->m_Engine->addRecent(path);
}
2021-02-18 06:31:03 -08:00
}
}
void UserInterface::addRecentMenuItems(std::vector<std::string> paths, bool addToEngine)
2021-02-18 06:31:03 -08:00
{
if (!this->recent_initialized) {
throw std::runtime_error("The UI is not ready in addRecent.");
}
2021-02-18 06:31:03 -08:00
for (std::vector<std::string>::iterator it = paths.begin() ; it != paths.end(); ++it) {
try {
this->addRecentMenuItem(*it, addToEngine);
}
catch (const std::runtime_error& ex) {
cerr << ex.what() << std::endl;
break;
}
2021-02-18 06:31:03 -08:00
}
}
bool UserInterface::hasRecent(std::string path)
{
bool enableVerbose = false;
if (this->m_Engine != nullptr) {
enableVerbose = this->m_Engine->m_EnableVerbose;
} else {
std::cerr << "Error: The engine is not ready in hasRecent." << std::endl;
}
if (!this->recent_initialized) {
throw std::runtime_error("The UI is not ready in addRecent.");
}
if (enableVerbose) {
std::cerr << " [hasRecent]" << std::endl;
std::cerr << " * checking children..." << std::endl;
}
// See http://irrlicht.sourceforge.net/docu/_i_g_u_i_element_8h_source.html#l00570
// core::list< IGUIElement * > Children = this->getChildren();
// ^ class UserInterface has no member named getChildren
// core::list< IGUIElement * > Children = this->recentMenu->getChildren();
// ^ gets no results
// core::list< IGUIElement * > Children = this->menu->getChildren();
// ^ only gets UIE_FILEMENU, UIE_PLAYBACKMENU, and UIE_VIEWMENU
std::string thisItemStr = " ";
for (u32 i = 0; i < this->recentMenu->getItemCount(); i++) {
thisItemStr = Utility::toString((wstring)this->recentMenu->getItemText(i));
// getItemText gets wchar_t*
// std::cerr << " * text:" << thisItemStr
// << std::endl;
if (thisItemStr == path)
return true;
}
return false;
// The commented section below is not valid since there are no children
// apparently, only items (inaccessible internals obtained by index).
/*
core::list< IGUIElement * > Children = this->recentMenu->getChildren();
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
{
irr::gui::IGUIElement* child = *it;
std::cerr << " * text:" << Utility::toString((std::wstring)child->getText())
<< " parent:" << Utility::toString((std::wstring)child->getParent()->getText())
<< " id:" << Utility::toString(child->getID())
<< std::endl;
// - getText is a const wchar_t*
// - getParent is an IGUIElement*
// (*it)->Parent;
}
// ^ gets no results
std::cerr << " * checking recent menu items for " << path << "..." << std::endl;
2021-02-18 06:31:03 -08:00
for (std::vector<u32>::iterator uiIt = this->recentIndices.begin() ; uiIt != this->recentIndices.end(); ++uiIt) {
// In the comments below, 1 is selected (idx) and 1101 is commandID.
// None of the commands work! An item is not an IGUI element apparently!
// IGUIContextMenu* child = this->recentMenu->getSubMenu(*uiIt);
// ^ null for 1 or 1101
// irr::gui::IGUIElement* child = this->recentMenu->getElementFromId(*uiIt, true);
// ^ null for 1101
// irr::gui::IGUIElement* child = this->menu->getElementFromId(*uiIt, true);
// ^ null for 1 or 1101
// irr::gui::IGUIElement* child = this->recentMenu->getElementFromId(*uiIt, true);
// ^ null for 1 or 1101
irr::gui::IGUIElement* child = this->getGUIEnvironment()->getRootGUIElement()->getElementFromId(1, true);
// ^ null for 1 or 1101
// ^ cast to the specific IGUI... interface before use!
// ^ See <http://irrlicht.sourceforge.net/forum/viewtopic.php?f=1&t=29322>
if (child != nullptr) {
std::cerr << " - " << *uiIt << " (1): " << Utility::toString(child->getText()) << std::endl;
if (Utility::toString(child->getText()) == path) {
return true;
}
}
else {
std::cerr << " - null at " << *uiIt << std::endl;
std::string uiItMsg = std::to_string(*uiIt);
// std::string uiItMsg = "<bad uiIt value in recentIndices: ";
// try {
// uiItMsg += std::to_string(*uiIt);
// }
// catch (const std::invalid_argument& ex) {
// uiItMsg += ex.what();
// }
// uiItMsg += ">";
const std::string msg = "There was no child for " + uiItMsg + " in hasRecent";
cerr << " " << msg << endl;
throw std::runtime_error(msg);
2021-02-18 06:31:03 -08:00
}
}
*/
2021-02-18 06:31:03 -08:00
return false;
}
bool UserInterface::openRecent(s32 commandID, s32 selectedItemID)
2021-02-18 06:31:03 -08:00
{
bool result = false;
if (!this->recent_initialized) {
2021-03-28 03:56:19 -07:00
throw std::runtime_error("The UI is not ready in openRecent.");
}
// IGUIElement* submenu = this->recentMenu->getElementFromId(commandID);
// ^ There is no element for menuID (such as 1100) nor for commandID (such as 1)
// IGUIElement* submenu = this->recentMenu->getSubMenu(selectedItemID);
// ^ There is no submenu for selectedItemID (such as 1)
// IGUIElement* submenu = this->recentMenu->getSubMenu(commandID);
// ^ There is no submenu for commandID (such as 1101)
// IGUIElement* submenu = this->menu->getElementFromId(commandID);
// ^ There is no elemend for commandID (such as 1101)
// IGUIElement* submenu = this->menu->getElementFromId(selectedItemID);
// ^ There is no element for selectedItemID (such as 1)
// IGUIElement* submenu = this->menu->getSubMenu(commandID);
// ^ There is no submenu for commandID (such as 1101)
IGUIElement* submenu = this->menu->getSubMenu(selectedItemID);
// ^ requires the sequential selectedItemID (NOT commandID specified on create)
if (submenu != nullptr) {
std::wstring menuText = this->recentMenu->getItemText(selectedItemID);
// std::string path = Utility::toString(submenu->getText());
// ^ blank
std::string path = Utility::toString(menuText); // blank
cerr << "path: " << path << endl;
cerr << "selectedItemID: " << selectedItemID << endl;
cerr << "menuText: " << Utility::toString(menuText) << endl;
result = m_Engine->loadMesh(menuText, true); // true to adjust recent menu order
if (!result) {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Load Mesh", L"The model is inaccessible or not in a compatible format.");
}
}
else {
cerr << "[UserInterface::openRecent] Error: There is no submenu for selectedItemID " << selectedItemID << std::endl;
// for (auto it : this->recentMenu->getChildren()) {
// cerr << " - " << it << std::endl;
// }
// ^ iterates 0 times; ranged for
}
return result;
2021-02-18 06:31:03 -08:00
}
2010-04-21 07:48:36 -07:00
// IEventReceiver
bool UserInterface::OnEvent(const SEvent& event)
2010-04-21 07:48:36 -07:00
{
// Note EventHandler::OnEvent calls other handlers for
// Certain event types (See ERT_3DVIEW there for instance).
2019-04-19 12:29:30 -07:00
bool handled = false;
if (event.EventType == EET_USER_EVENT) {
2019-04-19 12:29:30 -07:00
// debug() << "EET_USER_EVENT..." << endl;
if (event.UserEvent.UserData1 == UEI_WINDOWSIZECHANGED) {
2019-04-19 14:13:58 -07:00
if ((m_WindowSize.Width != m_Engine->m_Driver->getScreenSize().Width)
|| (m_WindowSize.Height != m_Engine->m_Driver->getScreenSize().Height)) {
snapWidgets();
}
2019-04-19 12:29:30 -07:00
handled = true;
}
} else if (event.EventType == EET_GUI_EVENT) {
// debug() << "EET_GUI_EVENT..." << endl;
handled = true; // set to false below if not handled
const SEvent::SGUIEvent* ge = &(event.GUIEvent);
2021-02-18 06:31:03 -08:00
s32 callerID = ge->Caller->getID();
switch (ge->EventType) {
// See http://irrlicht.sourceforge.net/docu/example009.html
case EGET_BUTTON_CLICKED:
switch(callerID) {
case UIE_PLAYBACKSTARTSTOPBUTTON:
this->m_Engine->toggleAnimation();
break;
case UIE_PLAYBACKINCREASEBUTTON:
this->m_Engine->incrementAnimationFPS(5);
break;
case UIE_PLAYBACKDECREASEBUTTON:
this->m_Engine->incrementAnimationFPS(-5);
break;
default:
cerr << "EGET_BUTTON_CLICKED wasn't expected from ID " << callerID << std::endl;
handled = false;
break;
2019-05-16 09:33:53 -07:00
}
case EGET_FILE_SELECTED:
this->m_Engine->m_View->m_MouseUser = "";
switch(callerID) {
case UIE_LOADFILEDIALOG:
{
IGUIFileOpenDialog* fileOpenDialog = static_cast<IGUIFileOpenDialog*>(ge->Caller);
wstring path = fileOpenDialog->getFileName();
bool result = false;
wstring extension = Utility::extensionOf(path);
if (Utility::toLower(Utility::toString(extension)) == "irr") {
result = m_Engine->loadScene(fileOpenDialog->getFileName());
}
else {
result = m_Engine->loadMesh(fileOpenDialog->getFileName(), true);
}
if (result) {
try {
2021-02-18 06:31:03 -08:00
this->addRecentMenuItem(Utility::toString(path), true);
}
catch (const std::runtime_error& ex) {
cerr << ex.what() << std::endl;
break;
}
}
if (!result) {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Load Mesh", L"The model is inaccessible or not in a compatible format.");
}
}
break;
case UIE_SAVEFILEDIALOG:
{
if (m_Engine->m_LoadedMesh != nullptr) {
IGUIFileOpenDialog* fileOpenDialog = static_cast<IGUIFileOpenDialog*>(ge->Caller);
// fileOpenDialog->getFileName()
m_Engine->saveMesh(fileOpenDialog->getDirectoryName(), "", "dae");
}
else {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Export", L"There is nothing to save.");
}
}
break;
case UIE_LOADTEXTUREDIALOG:
{
IGUIFileOpenDialog* fileOpenDialog = static_cast<IGUIFileOpenDialog*>(ge->Caller);
m_Engine->loadTexture(fileOpenDialog->getFileName(), true);
}
break;
default:
cerr << "EGET_FILE_SELECTED wasn't expected from ID: " << callerID << std::endl;
handled = false;
break;
2019-04-19 12:29:30 -07:00
}
case EGET_EDITBOX_ENTER:
switch (callerID) {
case UIE_PLAYBACKSETFRAMEEDITBOX:
if (this->m_Engine->m_LoadedMesh != nullptr) {
this->m_Engine->m_LoadedMesh->setCurrentFrame(
Utility::toF32(this->playbackSetFrameEditBox->getText())
);
}
break;
case UIE_PLAYBACKSTARTFRAMEEDITBOX:
if (this->m_Engine->m_LoadedMesh != nullptr) {
this->m_Engine->m_LoadedMesh->setFrameLoop(
Utility::toF32(this->playbackStartFrameEditBox->getText()),
Utility::toF32(this->playbackEndFrameEditBox->getText())
);
}
break;
case UIE_PLAYBACKENDFRAMEEDITBOX:
if (this->m_Engine->m_LoadedMesh != nullptr) {
this->m_Engine->m_LoadedMesh->setFrameLoop(
Utility::toF32(this->playbackStartFrameEditBox->getText()),
Utility::toF32(this->playbackEndFrameEditBox->getText())
);
}
break;
case UIE_TEXTUREPATHEDITBOX:
if (this->m_Engine->m_LoadedMesh != nullptr) {
this->m_Engine->loadTexture(texturePathEditBox->getText(), false);
}
break;
case UIE_FPSEDITBOX:
if (this->m_Engine->m_LoadedMesh != nullptr) {
this->m_Engine->m_LoadedMesh->setAnimationSpeed(
Utility::toF32(this->playbackFPSEditBox->getText())
);
}
break;
case UIE_AXISSIZEEDITBOX:
this->m_Engine->m_AxisLength = Utility::toF32(
this->axisSizeEditBox->getText()
2019-04-19 14:13:58 -07:00
);
break;
default:
cerr << "EGET_EDITBOX_ENTER isn't processed for ID: " << callerID << std::endl;
handled = false;
break;
2019-04-19 12:29:30 -07:00
}
case EGET_MESSAGEBOX_YES:
this->m_Engine->m_View->m_MouseUser = "";
break;
case EGET_MESSAGEBOX_NO:
this->m_Engine->m_View->m_MouseUser = "";
break;
case EGET_MESSAGEBOX_OK:
this->m_Engine->m_View->m_MouseUser = "";
break;
case EGET_MESSAGEBOX_CANCEL:
this->m_Engine->m_View->m_MouseUser = "";
break;
case EGET_FILE_CHOOSE_DIALOG_CANCELLED:
// ^ 12 usually
this->m_Engine->m_View->m_MouseUser = "";
break;
case EGET_ELEMENT_FOCUS_LOST:
// ^ 0 usually
switch (callerID) {
case UIE_LOADFILEDIALOG:
this->m_Engine->m_View->m_MouseUser = "";
break;
case UIE_SAVEFILEDIALOG:
this->m_Engine->m_View->m_MouseUser = "";
break;
default:
break;
}
debug() << callerID << " lost focus." << std::endl;
handled = false;
break;
case EGET_ELEMENT_FOCUSED:
switch (callerID) {
case UIE_LOADFILEDIALOG:
this->m_Engine->m_View->m_MouseUser = "UIE_LOADFILEDIALOG";
break;
case UIE_SAVEFILEDIALOG:
this->m_Engine->m_View->m_MouseUser = "UIE_SAVEFILEDIALOG";
break;
default:
break;
}
debug() << callerID << " got focus." << std::endl;
handled = false;
break;
case EGET_ELEMENT_HOVERED:
// debug() << "hovered over " << callerID << "." << std::endl;
handled = false;
break;
case EGET_ELEMENT_LEFT:
// debug() << "left " << callerID << "." << std::endl;
handled = false;
break;
case EGET_MENU_ITEM_SELECTED:
handled = handleMenuItemPressed(ge);
break;
case EGET_SCROLL_BAR_CHANGED:
handled = false;
break;
case EGET_COMBO_BOX_CHANGED:
handled = false;
break;
case EGET_ELEMENT_CLOSED:
debug() << "closed " << callerID << "." << std::endl;
handled = false;
break;
case EGET_DIRECTORY_SELECTED:
this->m_Engine->m_View->m_MouseUser = "";
handled = false;
break;
case EGET_EDITBOX_MARKING_CHANGED:
this->m_Engine->m_View->m_MouseUser = "edit box";
handled = false;
break;
default:
// EET_MOUSE_INPUT_EVENT EET_KEY_INPUT_EVENT EET_JOYSTICK_INPUT_EVENT
cerr << "[UserInterface] (verbose message) event.GUIEvent.EventType " << ge->EventType << " (See EGET_* in irrlicht/IEventReciever.h) is not handled (event.EventType is EET_GUI_EVENT)." << std::endl;
2021-02-18 06:31:03 -08:00
handled = false;
break;
}
} else if (event.EventType == EET_KEY_INPUT_EVENT) {
2019-04-19 12:29:30 -07:00
// debug() << "EET_KEY_INPUT_EVENT..." << endl;
handled = true; // set to false below if not handled
2019-04-19 14:16:28 -07:00
if (event.KeyInput.PressedDown
&& !m_Engine->KeyIsDown[event.KeyInput.Key]) {
if (event.KeyInput.Key == irr::KEY_F5) {
2019-04-19 14:13:58 -07:00
if (m_Engine->KeyIsDown[irr::KEY_LSHIFT]
|| m_Engine->KeyIsDown[irr::KEY_RSHIFT]) {
bool result = m_Engine->reloadTexture();
if (!result) {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Reload Texture", L"The texture is inaccessible or not in a compatible format.");
}
2019-04-19 12:29:30 -07:00
}
2019-05-16 09:33:53 -07:00
else {
if (m_Engine->m_LoadedMeshPath.length() > 0) {
2019-05-16 09:33:53 -07:00
bool result = m_Engine->reloadMesh();
if (!result) {
this->m_Engine->m_Device->getGUIEnvironment()->addMessageBox(
L"Reload Mesh", L"The model is inaccessible or not in a compatible format.");
}
}
else {
debug() << " - No mesh is loaded." << endl;
}
}
2019-04-19 12:29:30 -07:00
} else if (event.KeyInput.Key == irr::KEY_F3) {
2019-04-19 14:13:58 -07:00
if (m_Engine->KeyIsDown[irr::KEY_LSHIFT]
|| m_Engine->KeyIsDown[irr::KEY_RSHIFT]) {
2019-04-19 12:29:30 -07:00
loadNextTexture(-1);
debug() << " - back" << endl;
}
else
loadNextTexture(1);
} else if (event.KeyInput.Key == irr::KEY_KEY_I) {
2019-04-19 14:13:58 -07:00
if (m_Engine->KeyIsDown[irr::KEY_LCONTROL]
|| m_Engine->KeyIsDown[irr::KEY_RCONTROL]) {
// IGUIContextMenu* textureInterpolationElement = (
// dynamic_cast<IGUIContextMenu*>(
// viewMenu->getElementFromId(
// UIC_VIEW_TEXTURE_INTERPOLATION
// )
// );
// )
m_Engine->setEnableTextureInterpolation(
!m_Engine->getEnableTextureInterpolation()
);
viewMenu->setItemChecked(
viewTextureInterpolationIdx,
m_Engine->getEnableTextureInterpolation()
);
2019-04-19 12:29:30 -07:00
}
else
handled = false;
} else if (event.KeyInput.Key == irr::KEY_RIGHT) {
2019-04-19 14:13:58 -07:00
if (m_Engine->KeyIsDown[irr::KEY_LCONTROL]
|| m_Engine->KeyIsDown[irr::KEY_RCONTROL]) {
2019-04-19 12:29:30 -07:00
m_Engine->incrementAnimationFPS(5);
2019-03-07 21:52:29 -08:00
}
else {
incrementFrame(1.0f, true);
}
2019-04-19 12:29:30 -07:00
} else if (event.KeyInput.Key == irr::KEY_LEFT) {
2019-04-19 14:13:58 -07:00
if (m_Engine->KeyIsDown[irr::KEY_LCONTROL]
|| m_Engine->KeyIsDown[irr::KEY_RCONTROL]) {
2019-04-19 12:29:30 -07:00
m_Engine->incrementAnimationFPS(-5);
}
else {
incrementFrame(-1.0f, true);
}
} else if (event.KeyInput.Char == L' ') {
2019-03-07 21:52:29 -08:00
m_Engine->toggleAnimation();
}
2019-04-19 12:29:30 -07:00
else
handled = false;
2019-03-07 21:52:29 -08:00
// std::wcerr << "Char: " << event.KeyInput.Char << endl;
}
m_Engine->KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
2019-03-07 21:52:29 -08:00
return true;
} else if (event.EventType == EET_MOUSE_INPUT_EVENT) {
2019-04-19 12:29:30 -07:00
// debug() << "EET_MOUSE_INPUT_EVENT..." << endl;
handled = true; // set to false below if not handled
// TODO: improve this copypasta (or elsewhere use states 1 and 3 as
// events, and add 1 as "handled" (or set back to 0 for no drag feature)
// as intended for drag or long press handling).
switch (event.MouseInput.Event) {
case EMIE_LMOUSE_LEFT_UP:
if (m_Engine->LMouseState == 2) {
m_Engine->LMouseState = 3;
}
break;
case EMIE_LMOUSE_PRESSED_DOWN:
if (m_Engine->LMouseState == 0) {
m_Engine->LMouseState = 1;
}
break;
case EMIE_RMOUSE_LEFT_UP:
if (m_Engine->RMouseState == 2) {
m_Engine->RMouseState = 3;
}
break;
case EMIE_RMOUSE_PRESSED_DOWN:
if (m_Engine->RMouseState == 0) {
m_Engine->RMouseState = 1;
}
break;
2019-04-19 12:29:30 -07:00
default:
handled = false;
}
} else {
cerr << "[UserInterface] (verbose message) event.EventType " << event.EventType << " is ignored." << std::endl;
2010-04-21 07:48:36 -07:00
}
2019-04-19 12:29:30 -07:00
return handled;
2010-04-21 07:48:36 -07:00
}