diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..53c7673 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/Boxer"] + path = libs/Boxer + url = https://github.com/aaronmjacobs/Boxer.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 48c5c32..fcd579b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,9 @@ cmake_minimum_required( VERSION 3.0 ) project( Facecam2D VERSION 0.1.0 ) set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ) +add_subdirectory( "libs/Boxer" ) find_package( libzip REQUIRED ) +find_package( fmt REQUIRED ) find_package( WebP REQUIRED ) find_package( OpenCV REQUIRED ) message (STATUS "Found OpenCV at: " ${OpenCV_INCLUDE_DIRS} ) @@ -44,5 +46,7 @@ add_executable( fc2d src/model.cpp src/toml.c 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 ) diff --git a/libs/Boxer b/libs/Boxer new file mode 160000 index 0000000..01c17f6 --- /dev/null +++ b/libs/Boxer @@ -0,0 +1 @@ +Subproject commit 01c17f6a9cd66068f7890ea887ab3b9a673f0434 diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000..7be1ac2 --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,25 @@ +#include + +#ifndef NO_GRAPHICAL_DIALOG +#include +#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; +} diff --git a/src/error.hpp b/src/error.hpp new file mode 100644 index 0000000..1aab31a --- /dev/null +++ b/src/error.hpp @@ -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 diff --git a/src/graphics.cpp b/src/graphics.cpp index 939be01..02b172e 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -115,7 +115,7 @@ void initTexture (GLuint* texNum, unsigned char* buffer, size_t bufferLength) { //try stb_image (png, jpg, gif, etc) pixels = stbi_load_from_memory(buffer, bufferLength, &x, &y, &channels, 4); if (!pixels) { - std::cerr << "Corrupt or unsupported texture format!" << std::endl; + showError("Corrupt or unsupported texture format!", "Could not load texture", false); GLubyte defaultPixels[] = {255, 0, 255, 255, 0, 0, 0, 255, diff --git a/src/model.cpp b/src/model.cpp index 6f65fe0..06d9343 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -1,9 +1,11 @@ #include #include #include +#include #include //dynamically link tomlcpp if it becomes common in repositories #include #include +#include #define BUFFER_SIZE_MODEL_DESC 8192 // 8 KiB #define BUFFER_SIZE_TEXTURE 16777220 // 16 MiB @@ -21,7 +23,7 @@ void textureFromArchive(zip_t* archive, const char* path, ModelPart* part, size_ delete [] textureBuffer; } 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); 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; } @@ -43,13 +45,13 @@ Model::Model(const char* path) { // parse model.toml auto modelDesc = toml::parse(std::string(modelDescBuffer)); 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 auto format = modelDesc.table->getTable("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 { // get format version auto formatMajResult = format->getInt("version_major"); @@ -60,14 +62,14 @@ Model::Model(const char* path) { if (formatMajResult.second != SUPPORTED_MODEL_MAJOR || formatMinResult.second > SUPPORTED_MODEL_MINOR ) { - std::cerr << "Model format version " << - formatMajResult.second << "." << formatMinResult.second << - " is unsupported! This version of " << PROJECT_NAME << " supports model file version " << - SUPPORTED_MODEL_MAJOR << ".0 to version " << - SUPPORTED_MODEL_MAJOR << "." << SUPPORTED_MODEL_MINOR << std::endl; + showError(fmt::format("Model format version {0}.{1} is unsupported! This version of {2} supports model file version" + " {3}.0 to version {3}.{4}.", + formatMajResult.second, formatMinResult.second, + PROJECT_NAME, + SUPPORTED_MODEL_MAJOR, SUPPORTED_MODEL_MINOR), "Could not open model"); } } 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"); 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 { // get name auto nameResult = modelInfo->getString("name"); if (!nameResult.first) { - std::cerr << "Model does not have a name!" << std::endl; + showWarning("Model does not have a name!", "Model warning"); } else { name = nameResult.second; } @@ -99,9 +101,7 @@ Model::Model(const char* path) { // position auto bindResult = partsVec[i].getString("bind"); - if (!bindResult.first) { - std::cerr << "Part " << i << " does not define a bind!" << std::endl; - } else { + if (bindResult.first) { newPart.setBind(bindResult.second); } @@ -140,7 +140,8 @@ Model::Model(const char* path) { auto textureVec = *textureArray->getTableVector().get(); 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 { // a list of textures with triggers were defined for (int j = 0; j < textureVec.size(); j++) {