Add graphical error messages
Error and warning messages now show up as dialog boxes using Boxer. Additional dependencies: - fmt (for string formatting) - Boxer (static)
This commit is contained in:
parent
e56009c125
commit
3773532816
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "libs/Boxer"]
|
||||||
|
path = libs/Boxer
|
||||||
|
url = https://github.com/aaronmjacobs/Boxer.git
|
|
@ -1,7 +1,9 @@
|
||||||
cmake_minimum_required( VERSION 3.0 )
|
cmake_minimum_required( VERSION 3.0 )
|
||||||
project( Facecam2D VERSION 0.1.0 )
|
project( Facecam2D VERSION 0.1.0 )
|
||||||
set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake )
|
set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake )
|
||||||
|
add_subdirectory( "libs/Boxer" )
|
||||||
find_package( libzip REQUIRED )
|
find_package( libzip REQUIRED )
|
||||||
|
find_package( fmt REQUIRED )
|
||||||
find_package( WebP REQUIRED )
|
find_package( WebP REQUIRED )
|
||||||
find_package( OpenCV REQUIRED )
|
find_package( OpenCV REQUIRED )
|
||||||
message (STATUS "Found OpenCV at: " ${OpenCV_INCLUDE_DIRS} )
|
message (STATUS "Found OpenCV at: " ${OpenCV_INCLUDE_DIRS} )
|
||||||
|
@ -44,5 +46,7 @@ add_executable( fc2d
|
||||||
src/model.cpp
|
src/model.cpp
|
||||||
src/toml.c
|
src/toml.c
|
||||||
src/tomlcpp.cpp
|
src/tomlcpp.cpp
|
||||||
|
src/error.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries( fc2d ${OpenCV_LIBS} ${OPENGL_LIBRARIES} ${WEBP_LIBRARIES} FreeGLUT::freeglut GLEW::glew zip )
|
target_link_libraries( fc2d ${OpenCV_LIBS} ${OPENGL_LIBRARIES} ${WEBP_LIBRARIES}
|
||||||
|
FreeGLUT::freeglut GLEW::glew zip Boxer fmt )
|
||||||
|
|
1
libs/Boxer
Submodule
1
libs/Boxer
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 01c17f6a9cd66068f7890ea887ab3b9a673f0434
|
25
src/error.cpp
Normal file
25
src/error.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifndef NO_GRAPHICAL_DIALOG
|
||||||
|
#include <boxer/boxer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void showError(std::string message, std::string titleMessage, bool fatal = true) {
|
||||||
|
#ifndef NO_GRAPHICAL_DIALOG
|
||||||
|
boxer::show(message.c_str(), titleMessage.c_str(), boxer::Style::Error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::cerr << "ERROR: " << message << std::endl;
|
||||||
|
|
||||||
|
if (fatal) {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showWarning(std::string message, std::string titleMessage) {
|
||||||
|
#ifndef NO_GRAPHICAL_DIALOG
|
||||||
|
boxer::show(message.c_str(), titleMessage.c_str(), boxer::Style::Warning);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::cerr << "WARN: " << message << std::endl;
|
||||||
|
}
|
7
src/error.hpp
Normal file
7
src/error.hpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef ERROR_HPP
|
||||||
|
#define ERROR_HPP
|
||||||
|
|
||||||
|
void showError(std::string message, std::string titleMessage, bool fatal = true);
|
||||||
|
void showWarning(std::string message, std::string titleMessage);
|
||||||
|
|
||||||
|
#endif
|
|
@ -115,7 +115,7 @@ void initTexture (GLuint* texNum, unsigned char* buffer, size_t bufferLength) {
|
||||||
//try stb_image (png, jpg, gif, etc)
|
//try stb_image (png, jpg, gif, etc)
|
||||||
pixels = stbi_load_from_memory(buffer, bufferLength, &x, &y, &channels, 4);
|
pixels = stbi_load_from_memory(buffer, bufferLength, &x, &y, &channels, 4);
|
||||||
if (!pixels) {
|
if (!pixels) {
|
||||||
std::cerr << "Corrupt or unsupported texture format!" << std::endl;
|
showError("Corrupt or unsupported texture format!", "Could not load texture", false);
|
||||||
|
|
||||||
GLubyte defaultPixels[] =
|
GLubyte defaultPixels[] =
|
||||||
{255, 0, 255, 255, 0, 0, 0, 255,
|
{255, 0, 255, 255, 0, 0, 0, 255,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <tomlcpp.hpp> //dynamically link tomlcpp if it becomes common in repositories
|
#include <tomlcpp.hpp> //dynamically link tomlcpp if it becomes common in repositories
|
||||||
#include <model.hpp>
|
#include <model.hpp>
|
||||||
#include <config.hpp>
|
#include <config.hpp>
|
||||||
|
#include <error.hpp>
|
||||||
|
|
||||||
#define BUFFER_SIZE_MODEL_DESC 8192 // 8 KiB
|
#define BUFFER_SIZE_MODEL_DESC 8192 // 8 KiB
|
||||||
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
|
#define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB
|
||||||
|
@ -21,7 +23,7 @@ void textureFromArchive(zip_t* archive, const char* path, ModelPart* part, size_
|
||||||
|
|
||||||
delete [] textureBuffer;
|
delete [] textureBuffer;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << path << " does not exist in archive!" << std::endl;
|
showError(fmt::format("Texture file \"{}\" does not exist in archive!", path), "Could not open model");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ Model::Model(const char* path) {
|
||||||
zip_t* archive = zip_open(path, ZIP_RDONLY, &zipError);
|
zip_t* archive = zip_open(path, ZIP_RDONLY, &zipError);
|
||||||
|
|
||||||
if (!archive) {
|
if (!archive) {
|
||||||
std::cerr << "Model file " << path << " does not exist!" << std::endl;
|
showError(fmt::format("Model file {} does not exist or is corrupt!", path), "Could not open model");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +45,13 @@ Model::Model(const char* path) {
|
||||||
// parse model.toml
|
// parse model.toml
|
||||||
auto modelDesc = toml::parse(std::string(modelDescBuffer));
|
auto modelDesc = toml::parse(std::string(modelDescBuffer));
|
||||||
if (!modelDesc.table) {
|
if (!modelDesc.table) {
|
||||||
std::cerr << "cannot parse model.toml! " << std::endl << modelDesc.errmsg << std::endl;
|
showError("Cannot parse model.toml:\n" + modelDesc.errmsg, "Could not open model");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get format table
|
// get format table
|
||||||
auto format = modelDesc.table->getTable("format");
|
auto format = modelDesc.table->getTable("format");
|
||||||
if (!format) {
|
if (!format) {
|
||||||
std::cerr << "Model does not have a format table!" << std::endl;
|
showError("Model does not have a format table!", "Could not open model");
|
||||||
} else {
|
} else {
|
||||||
// get format version
|
// get format version
|
||||||
auto formatMajResult = format->getInt("version_major");
|
auto formatMajResult = format->getInt("version_major");
|
||||||
|
@ -60,14 +62,14 @@ Model::Model(const char* path) {
|
||||||
if (formatMajResult.second != SUPPORTED_MODEL_MAJOR ||
|
if (formatMajResult.second != SUPPORTED_MODEL_MAJOR ||
|
||||||
formatMinResult.second > SUPPORTED_MODEL_MINOR ) {
|
formatMinResult.second > SUPPORTED_MODEL_MINOR ) {
|
||||||
|
|
||||||
std::cerr << "Model format version " <<
|
showError(fmt::format("Model format version {0}.{1} is unsupported! This version of {2} supports model file version"
|
||||||
formatMajResult.second << "." << formatMinResult.second <<
|
" {3}.0 to version {3}.{4}.",
|
||||||
" is unsupported! This version of " << PROJECT_NAME << " supports model file version " <<
|
formatMajResult.second, formatMinResult.second,
|
||||||
SUPPORTED_MODEL_MAJOR << ".0 to version " <<
|
PROJECT_NAME,
|
||||||
SUPPORTED_MODEL_MAJOR << "." << SUPPORTED_MODEL_MINOR << std::endl;
|
SUPPORTED_MODEL_MAJOR, SUPPORTED_MODEL_MINOR), "Could not open model");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Model does not define a format version!" << std::endl;
|
showError("Model does not define a format version!", "Could not open model");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,14 +78,14 @@ Model::Model(const char* path) {
|
||||||
auto modelInfo = modelDesc.table->getTable("model_info");
|
auto modelInfo = modelDesc.table->getTable("model_info");
|
||||||
|
|
||||||
if (!modelInfo) {
|
if (!modelInfo) {
|
||||||
std::cerr << "Model does not have a model_info table!" << std::endl;
|
showError("Model does not have a model_info table!", "Could not open model");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// get name
|
// get name
|
||||||
auto nameResult = modelInfo->getString("name");
|
auto nameResult = modelInfo->getString("name");
|
||||||
|
|
||||||
if (!nameResult.first) {
|
if (!nameResult.first) {
|
||||||
std::cerr << "Model does not have a name!" << std::endl;
|
showWarning("Model does not have a name!", "Model warning");
|
||||||
} else {
|
} else {
|
||||||
name = nameResult.second;
|
name = nameResult.second;
|
||||||
}
|
}
|
||||||
|
@ -99,9 +101,7 @@ Model::Model(const char* path) {
|
||||||
|
|
||||||
// position
|
// position
|
||||||
auto bindResult = partsVec[i].getString("bind");
|
auto bindResult = partsVec[i].getString("bind");
|
||||||
if (!bindResult.first) {
|
if (bindResult.first) {
|
||||||
std::cerr << "Part " << i << " does not define a bind!" << std::endl;
|
|
||||||
} else {
|
|
||||||
newPart.setBind(bindResult.second);
|
newPart.setBind(bindResult.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,8 @@ Model::Model(const char* path) {
|
||||||
auto textureVec = *textureArray->getTableVector().get();
|
auto textureVec = *textureArray->getTableVector().get();
|
||||||
|
|
||||||
if (textureVec.size() < 1) {
|
if (textureVec.size() < 1) {
|
||||||
std::cerr << "Part " << i << " does not define any textures!" << std::endl;
|
showWarning(fmt::format("Part {} does not define any textures! Parts with no textures defined will"
|
||||||
|
" show a default \"missing texture\" pattern.", i), "Model warning");
|
||||||
} else {
|
} else {
|
||||||
// a list of textures with triggers were defined
|
// a list of textures with triggers were defined
|
||||||
for (int j = 0; j < textureVec.size(); j++) {
|
for (int j = 0; j < textureVec.size(); j++) {
|
||||||
|
|
Loading…
Reference in a new issue