diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f60158..9473a5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,13 +48,12 @@ if(NB_BUILD_TESTS) include(FetchContent) FetchContent_Declare( gtest - URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip + URL https://github.com/google/googletest/archive/refs/heads/main.zip ) if (WIN32) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(gtest) endif() - include(GoogleTest) set(GTEST_COLOR ON) set(GLFW_BUILD_TESTS ON CACHE BOOL "" FORCE) endif() @@ -62,6 +61,7 @@ endif() if (NB_LOGGING) message(STATUS "Building with automatic logging") add_compile_definitions(_NB_AUTOLOG) + add_compile_definitions(_NB_CODE_ERROR_LOCATIONS) endif() if (NB_TARGET_WINDOWS) diff --git a/engine/NBCore/ANSITerm.hpp b/engine/NBCore/ANSITerm.hpp index b74ee7b..b729bac 100644 --- a/engine/NBCore/ANSITerm.hpp +++ b/engine/NBCore/ANSITerm.hpp @@ -2,16 +2,10 @@ #ifndef _NB_ANSI_TERM #define _NB_ANSI_TERM -#include -#include -#include #include #include -#include #include -#include "TypeTraits.hpp" - /* ----------- TECH DEBT ------------ Idk wtf to do here. This was originally to allow me to print diff --git a/engine/NBCore/CMakeLists.txt b/engine/NBCore/CMakeLists.txt index 6b105df..76e9249 100644 --- a/engine/NBCore/CMakeLists.txt +++ b/engine/NBCore/CMakeLists.txt @@ -1,20 +1,23 @@ -include_directories(./.) - toAbsolutePath(NB_CORE_SOURCE - ./src/Errors.cpp - ./src/Processes.cpp + ./src/ErrorsImpl.cpp ./src/Logger.cpp + ./src/Processes.cpp + ./src/StringUtils.cpp + ./src/Utils.cpp ) toAbsolutePath(NB_CORE_INCLUDE ./ANSITerm.hpp ./DataSink.hpp ./Errors.hpp + ./ErrorsImpl.hpp ./Logger.hpp ./Processes.hpp + ./StringUtils.hpp ./ThreadsafeQueue.hpp ./Types.hpp ./TypeTraits.hpp + ./Utils.hpp ) set(NB_CORE_SOURCE ${NB_CORE_SOURCE} PARENT_SCOPE) @@ -22,6 +25,8 @@ set(NB_CORE_INCLUDE ${NB_CORE_INCLUDE} PARENT_SCOPE) add_library(NBCore ${NB_CORE_SOURCE}) +target_include_directories(NBCore PUBLIC ./.) + if (NB_BUILD_TESTS) add_subdirectory(./tests) endif() \ No newline at end of file diff --git a/engine/NBCore/DataSink.hpp b/engine/NBCore/DataSink.hpp index 75cbad6..155c48a 100644 --- a/engine/NBCore/DataSink.hpp +++ b/engine/NBCore/DataSink.hpp @@ -2,6 +2,7 @@ #ifndef _NB_DATASINK #define _NB_DATASINK +#include #include #include "ThreadSafeQueue.hpp" @@ -34,9 +35,9 @@ class BufferedDataProcessor : public DataSink { public: using Base::Base; - bool stop() noexcept { return type_ptr->stop(); } - bool run() { return type_ptr->run(); } - bool in(const DataType& val) { return type_ptr->in(val); } + virtual bool stop() noexcept override { return type_ptr->stop(); } + virtual bool run() override { return type_ptr->run(); } + virtual bool in(const DataType& val) override { return type_ptr->in(val); } protected: unsigned int count() const { @@ -71,13 +72,13 @@ template class MultithreadedDataProcessor : public BufferedDataProcessor, ProcessorType> { using Base = BufferedDataProcessor, ProcessorType>; -public: + public: ~MultithreadedDataProcessor() { type_ptr->stop(); } bool isRunning() const noexcept override { - return this->_running && (_runningThread!=nullptr); + return this->_running; } - bool run() { + bool run() override { if (!type_ptr->isRunning()) { this->_running = true; _runningThread = std::make_shared([&]{ @@ -97,7 +98,7 @@ public: return !type_ptr->isRunning(); } -protected: + protected: using Base::Base; unsigned int count() const { diff --git a/engine/NBCore/Errors.hpp b/engine/NBCore/Errors.hpp index a893b9b..8a6db60 100644 --- a/engine/NBCore/Errors.hpp +++ b/engine/NBCore/Errors.hpp @@ -2,138 +2,42 @@ #ifndef _NB_ERROR #define _NB_ERROR -#include -#include -#include -#include -#include - -#ifndef THROW_WITH_INFO - #ifdef CODE_ERROR_LOCATIONS - #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) - #else - #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__) - #endif // CODE_ERROR_LOCATIONS -#endif // THROW_WITH_INFO - -#ifndef THROW - #ifdef LOGGING - #define THROW_WTIH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) - #else - #define THROW(...) THROW_WITH_INFO(__VA_ARGS__) - #endif // LOGGING -#endif // THROW +#include "ErrorsImpl.hpp" +#include "Logger.hpp" namespace nb { -typedef std::unordered_map ErrorCodeMap; - -template -class ErrorBase : public std::exception { -public: - ErrorBase( - const unsigned int code, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase( - code, - ErrorBase::lookup(code), - line, - filename - ) {} - ErrorBase( - const unsigned int code, - const std::exception& trace, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase( - code, - ErrorBase::lookup(code), - trace, - line, - filename - ) {} - - static std::string lookup(unsigned int); - unsigned int code() const noexcept { - return static_cast(this)->_code; - }; - virtual const char* what() const noexcept override final { return _msg.c_str(); }; - -protected: - ErrorBase() = default; - ErrorBase( - const unsigned int code, - std::string msg, - unsigned int line=0, - std::string filename="" - ) noexcept : _code{code}, _msg{""} { - static_assert(std::is_same, decltype(ErrorType::ErrorMessages)>::value, - "const std::unordered_map ErrorMessages must be " - "a class member." - ); - static_assert(std::is_enum_v, "enum ErrorCodes must be a class member."); - static_assert(std::is_same, unsigned int>::value, - "enum ErrorCodes must be of underlying type unsigned int." - ); - static_assert(std::is_same::value, - "const std::string type must be a class member." - ); - - _msg += std::string(ErrorType::type); - _msg += "[" + std::to_string(_code) + "]"; - if (line && filename.size()) { - _msg += " in \'" + filename + "\':" + std::to_string(line); - } - _msg += "\n " + msg; - } - ErrorBase( - const unsigned int code, - std::string msg, - const std::exception& trace, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase(code, msg, line, filename) { - std::string what_msg(trace.what()); - std::string::size_type newline_pos=-1; - while((newline_pos=what_msg.find("\n", newline_pos+1))!= std::string::npos) { - what_msg.replace(newline_pos, 1, "\n "); - } - _msg += "\n Trace - " + what_msg; - } - - unsigned int _code; - std::string _msg; -}; - -class Error : public ErrorBase { -public: - enum ErrorCodes : unsigned int { - GENERAL, UNDEFINED, BADERRORCODE - }; - - using ErrorBase::ErrorBase; - - friend ErrorBase; - -protected: - static const std::string type; - static const ErrorCodeMap ErrorMessages; - -}; - -template -std::string ErrorBase::lookup(unsigned int code) { - for (auto kv : ErrorType::ErrorMessages) { - if (kv.first==code) { - return std::string(kv.second); - } - } - - throw Error(Error::ErrorCodes::BADERRORCODE); -} +#ifdef _NB_AUTOLOG + #ifdef _NB_CODE_ERROR_LOCATIONS + #ifndef LOG + #define LOG(args...) nb::logger.log(args, __FILE__, __LINE__) + #endif // LOG + #ifndef WARN + #define WARN(args...) nb::logger.warn(args, __FILE__, __LINE__) + #endif // WARN + #ifndef ERROR + #define ERROR(args...) nb::logger.error(args, __FILE__, __LINE__) + #endif // ERROR + #else + #ifndef LOG + #define LOG(args...) nb::logger.log(args) + #endif // LOG + #ifndef WARN + #define WARN(args...) nb::logger.warn(args) + #endif // WARN + #ifndef ERROR + #define ERROR(args...) nb::logger.error(args) + #endif // ERROR + #endif // _NB_CODE_ERROR_LOCATIONS +#endif // _NB_AUTOLOG +#ifndef THROW + #ifdef _NB_AUTOLOG + #define THROW(args...) ERROR(args); nb::logger.stop(); throw args + #else + #define THROW(args...) throw args + #endif // _NB_CODE_ERROR_LOCATIONS +#endif // THROW } // namespace nb - #endif // _NB_ERROR \ No newline at end of file diff --git a/engine/NBCore/ErrorsImpl.hpp b/engine/NBCore/ErrorsImpl.hpp new file mode 100644 index 0000000..c5b8589 --- /dev/null +++ b/engine/NBCore/ErrorsImpl.hpp @@ -0,0 +1,175 @@ +#pragma once +#ifndef _NB_ERRORS_IMPL +#define _NB_ERRORS_IMPL + +#include +#include +#include +#include +#include + +#include "StringUtils.hpp" +#include "TypeTraits.hpp" + +namespace nb { + +typedef std::unordered_map ErrorCodeMap; + +template +class Error; + +class ErrorBase { + protected: + + public: + const unsigned int code; + const std::string msg; + const std::string type; + const std::shared_ptr trace; + + ErrorBase(const ErrorBase&) = default; + ErrorBase(const std::exception&) noexcept; + virtual std::string what() const noexcept { + std::string ret = msg; + if (trace) { + std::string trace_msg = msg; + ret += nb::NEWLINE + indent_strblock( + trace_msg, + nb::TABOVER, + nb::TABOVER+"Trace: " + ); + } + return ret; + } + + protected: + ErrorBase( + unsigned int code_, + std::string msg_, + std::string type_, + std::shared_ptr trace_ +) noexcept : + code(code_), + msg(msg_), + type(type_), + trace{trace_} +{} +}; + +template +class Error : public ErrorBase { + private: + void inline check_asserts() { + static_assert(std::is_same::value, + "const std::unordered_map ErrorMessages must be " + "a class member." + ); + static_assert(std::is_enum_v, "enum Codes must be a class member."); + static_assert(std::is_same, unsigned int>::value, + "enum Codes must be of underlying type unsigned int." + ); + static_assert(std::is_same::value, + "const std::string type must be a class member." + ); + } + + protected: + using ErrorBase::ErrorBase; + + public: + Error( + unsigned int code_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_), + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error( + std::string msg_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + 0, + msg_, + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error(unsigned int code_) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_), + ErrorType::type, + nullptr + ) { check_asserts(); } + + Error(std::string msg_) noexcept : ErrorBase( + 0, + msg_, + ErrorType::type, + nullptr + ) { check_asserts(); } + + Error( + unsigned int code_, + const std::string& note_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error(unsigned int code_, const std::string& note_) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", + ErrorType::type, + nullptr + ) { check_asserts(); } + + using ErrorBase::what; + using ErrorBase::code; + using ErrorBase::msg; + using ErrorBase::trace; + using ErrorBase::type; +}; + + +template<> +class Error : public Error> { + using Base = Error>; + + public: + using Base::Base; + + Error(unsigned int code_=1) noexcept : Base(code_) {} + + Error(const std::exception& err) : Base( + Codes::STANDARD, + std::string(err.what()), + "std::exception", + nullptr + ) {} + + // fix + Error(std::string msg_) noexcept : Base( + Codes::UNDEFINED, + msg_ + ) {} + + enum Codes : unsigned int { + STANDARD, UNDEFINED + }; + + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; +} // namespace nb + +#endif // _NB_ERRORS_IMPL \ No newline at end of file diff --git a/engine/NBCore/Logger.hpp b/engine/NBCore/Logger.hpp index e767eea..f4e79be 100644 --- a/engine/NBCore/Logger.hpp +++ b/engine/NBCore/Logger.hpp @@ -2,35 +2,27 @@ #ifndef _NB_LOGGER #define _NB_LOGGER -#include #include -#include #include #include -#include #include #include #include "DataSink.hpp" +#include "ErrorsImpl.hpp" #include "Processes.hpp" #include "ThreadSafeQueue.hpp" #include "TypeTraits.hpp" namespace nb { +class ErrorBase; + typedef std::chrono::time_point< std::chrono::system_clock, std::chrono::nanoseconds > LoggerTimePoint; -struct LogEvent{ - const LoggerTimePoint time; - const unsigned char lvl; - const std::string msg; - const std::thread::id tid; - const uint64_t pid; -}; - typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&); typedef std::unordered_map LogProcessFunctionMap; @@ -40,26 +32,36 @@ class LoggerBase using StreamType = ST; using LoggerType = Logger; using Base = MultithreadedDataProcessor; -public: - bool run() { - if (!type_ptr->isRunning()) { + + public: + bool run() override { + if (!static_cast(this)->isRunning()) { this->_running = true; this->_runningThread = std::make_shared([&]{ - while(type_ptr->isRunning()) { - type_ptr->flush(); + while(static_cast(this)->isRunning()) { + static_cast(this)->flush(); } - type_ptr->flush(); + static_cast(this)->flush(); }); } - return type_ptr->isRunning(); + return static_cast(this)->isRunning(); } -protected: + using Base::flush; + + protected: LoggerBase() = default; StreamType _ostream; +}; -private: - LoggerType* type_ptr = static_cast(this); +struct LogEvent{ + const LoggerTimePoint time; + const unsigned char lvl; + const std::string msg; + const std::thread::id tid; + const uint64_t pid; + const std::string file=""; + const unsigned int line=0; }; template @@ -72,39 +74,70 @@ public: template DebugLogger(ST&... streams) : _ostream(nb::RefPackToPtrVec(streams...).vec) {} - ~DebugLogger() { type_ptr->stop(); } + ~DebugLogger() { static_cast(this)->stop(); } - void log(const std::string& msg, const uint8_t& lvl=0xFF) { - type_ptr->push(LogEvent{ + template + void log( + U val, + std::string file="", + unsigned int line=0 + ) { static_cast(this)->write_message(val, 0x00, file, line); } + + template + void warn( + U val, + uint8_t lvl=0x01, + std::string file="", + unsigned int line=0 + ) { static_cast(this)->write_message(val, lvl, file, line); } + + void error( + const ErrorBase& val, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(val, 0xFF, file, line); + static_cast(this)->flush(); + } + + protected: + std::vector _ostream; + void write_message( + std::string msg, + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->push(LogEvent{ std::chrono::system_clock::now(), lvl, msg, std::this_thread::get_id(), GetPID(), + file, + line }); } template - void log(char const(&msg) [N], const uint8_t& lvl=0xFF) { - type_ptr->log(std::string(msg), lvl); + void write_message( + char const(&msg) [N], + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(std::string(msg), lvl, file, line); } - void log(const std::exception& err, const uint8_t& lvl=0xFF) { - type_ptr->log(err.what(), lvl); + void write_message( + const ErrorBase& err, + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(err.what(), lvl, file, line); } - template - void warn(const U& val, const uint8_t& lvl=0x01) { type_ptr->log(val, lvl); } - - template - void error(const U& val) { type_ptr->log(val, 0xFF); } - -protected: - std::vector _ostream; - -private: - LoggerType* type_ptr = static_cast(this); - }; class DefaultDebugLogger : public DebugLogger { @@ -114,7 +147,7 @@ class DefaultDebugLogger : public DebugLogger { template struct LogRow; -public: + public: using Base::Base; ~DefaultDebugLogger() { stop(); } @@ -127,17 +160,17 @@ public: protected: using Base::_ostream; - bool process(const LogEvent& msg) { - for (const auto os : this->_ostream) { - *os << msg.lvl << "\t|\t" << msg.msg << "\n"; - } - return true; - } + bool process(const LogEvent& msg); }; +extern const bool LOGGER_RUNNING; + +#ifndef _NB_NO_LOGGER extern DefaultDebugLogger logger; +#endif // _NB_NO_LOGGER // Taking Charge of Adult ADHD by Russell Barkley } // namespace nb + #endif // _NB_LOGGER \ No newline at end of file diff --git a/engine/NBCore/Processes.hpp b/engine/NBCore/Processes.hpp index 87ee029..87c16ee 100644 --- a/engine/NBCore/Processes.hpp +++ b/engine/NBCore/Processes.hpp @@ -7,6 +7,8 @@ namespace nb { uint64_t GetPID(); +uint64_t getTID(); + } // namespace nb diff --git a/engine/NBCore/StringUtils.hpp b/engine/NBCore/StringUtils.hpp new file mode 100644 index 0000000..b8d9778 --- /dev/null +++ b/engine/NBCore/StringUtils.hpp @@ -0,0 +1,79 @@ +#pragma once +#ifndef _NB_STRING_UTILS +#define _NB_STRING_UTILS + +#include +#include +#include + +#include "TypeTraits.hpp" + +namespace nb { + +#ifdef _NB_TARGET_WINDOWS + const std::string NEWLINE = "\n"; + const std::wstring WNEWLINE = L"\n"; +#endif // _NB_TARGET_WINDOWS +#ifdef _NB_TARGET_LINUX + const std::string NEWLINE = "\n"; + const std::wstring WNEWLINE = L"\n"; +#endif // _NB_TARGET_LINUX + +const std::string TABOVER = " "; + +// std::wstring str_to_wstr(std::string in); + +// std::string wstr_to_str(std::wstring in); + +template +void stream(const Stream& s, Args&&... args); + +template +void stream(const Stream& s, Args&&... args) { + (s << ... << args); +} + +template +void term(Args&&... args) { stream(std::cout, args..., nb::NEWLINE); } + +template +void wterm(Args&&... args) { stream(std::wcout, args..., nb::WNEWLINE); } + +template +std::basic_string find_and_replace( + ExplicitType_t> original, + ExplicitType_t> find, + ExplicitType_t> replace +) { + using StringType = std::basic_string; + + StringType ret(original); + + std::size_t find_len = find.length(); + std::size_t replace_len = replace.length(); + std::size_t currpos = 0; + while(true) { + currpos = ret.find(find, currpos); + if (currpos == StringType::npos) { + break; + } + ret = ret.erase(currpos, find_len); + ret = ret.insert(currpos, replace); + currpos += replace_len; + } + return ret; +} + +std::string indent_strblock( + std::string block, + std::string prepend, + std::string topIndent +); + +std::string indent_strblock( + std::string block, + std::string prepend +); + +} // namespace nb +#endif // _NB_STRING_UTILS \ No newline at end of file diff --git a/engine/NBCore/TypeTraits.hpp b/engine/NBCore/TypeTraits.hpp index 5226e94..fb990fe 100644 --- a/engine/NBCore/TypeTraits.hpp +++ b/engine/NBCore/TypeTraits.hpp @@ -2,15 +2,96 @@ #ifndef _NB_TYPE_TRAITS #define _NB_TYPE_TRAITS +#include #include +#include namespace nb { -template +namespace detail { + template < + class Default, + class AlwaysVoid, + template class Op, + class... Args + > + struct detector { + using value = std::false_type; + using type = Default; + }; + + template < + class Default, + template class Op, + class... Args + > + struct detector>, Op, Args...> { + using value = std::true_type; + using type = Op; + }; +} // namespace detail + +struct NoneType{}; + +template < + template class Op, + class... Args +> +using is_detected = typename detail::detector::value; + +template +struct ValidConversion; + +template +struct ValidConversion { + typedef std::conjunction< + std::is_constructible, std::is_constructible, std::conjunction...> + > value_type; + static constexpr bool value = value_type::value; + typedef std::enable_if_t to; + +}; + +template +struct ValidConversion { + typedef std::is_constructible value_type; + static constexpr bool value = value_type::value; + typedef std::enable_if_t to; + typedef std::enable_if_t from; +}; + +template +using ValidConversion_v = typename ValidConversion::value; + +template +using ValidConversion_to = typename ValidConversion::to; + +template +using ValidConversion_from = typename ValidConversion::from; + +template +struct ExplicitType { using type=T; }; + +template +using ExplicitType_t = typename ExplicitType::type; + +template +T* NULLPTR = static_cast(nullptr); + +template +struct RunAndOutput { + using type = T; + T value; + RunAndOutput(Func func, T val) : value(val) { + func(); + } +}; + +template inline typename std::enable_if::type ForEach(std::tuple&, Func) {} -template +template inline typename std::enable_if::type ForEach(std::tuple& tup, Func f) { f(N, std::get(tup)); diff --git a/engine/NBCore/Types.hpp b/engine/NBCore/Types.hpp deleted file mode 100644 index 293bdb5..0000000 --- a/engine/NBCore/Types.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#ifndef _NB_CORE_TYPES -#define _NB_CORE_TYPES - -#include - -namespace nb { - -template -T swapEndian(const T& val) { - T ret; - const int size = sizeof(T); - auto retLoc = static_cast(&ret); - auto valLoc = static_cast(&val); - - for (int i = 0; i < size; ++i) { - memcpy(retLoc+i, valLoc+(size-i-1), 1); - } - return ret; -} - -using ByteVector = std::vector; - -} // namespace nb -#endif // _NB_CORE_TYPES \ No newline at end of file diff --git a/engine/NBCore/Utils.hpp b/engine/NBCore/Utils.hpp new file mode 100644 index 0000000..76ae1ed --- /dev/null +++ b/engine/NBCore/Utils.hpp @@ -0,0 +1,118 @@ +#pragma once +#ifndef _NB_CORE_TYPES +#define _NB_CORE_TYPES + +#include +#include + +#include "Errors.hpp" + +namespace nb { + +template +using SharedVector = std::vector>; + +template +using RValueVector = std::vector; + +using ByteVector = std::vector; + +class ObjectManagerError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, NO_MANAGER, MANAGER_MISMATCH, LOCK_OVERFLOW, BAD_THREAD + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +template +ByteVector vectorToBytes(const std::vector& vec) { + unsigned int num_bytes = vec.size() * sizeof(T); + ByteVector ret(num_bytes); + memcpy(ret.data(), vec.data(), num_bytes); + return ret; +} + +template +ByteVector concatVectorBytes(const std::vector& vec1, const std::vector& vec2) { + ByteVector vec1_raw = vectorToBytes(vec1); + ByteVector vec2_raw = vectorToBytes(vec2); + unsigned int vec1_raw_size = vec1_raw.size(); + unsigned int vec2_raw_size = vec2_raw.size(); + ByteVector ret(vec1_raw_size + vec2_raw_size); + memcpy(ret.data(), vec1_raw.data(), vec1_raw_size); + memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size); + return ret; +} + +template +std::vector bytesToVector(const ByteVector& vec) { + if (vec.size() % sizeof(T) != 0) { + THROW(std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">.")); + } + unsigned int num_elmts = vec.size() / sizeof(T); + std::vector ret(num_elmts); + memcpy(ret.data(), vec.data(), vec.size()); + + return ret; +} + +template +class ThreadsafeObjectLock; + +template +class ThreadsafeObject { + using Codes = ObjectManagerError::Codes; + + public: + ThreadsafeObject(T&& obj) + : _obj(std::make_shared(obj)) {} + + ThreadsafeObjectLock lock() { + return ThreadsafeObjectLock(this); + } + + friend ThreadsafeObjectLock; + + protected: + std::shared_ptr _obj; + mutable std::recursive_mutex _mutex; + +}; + +template +class ThreadsafeObjectLock { + public: + ThreadsafeObjectLock(const ThreadsafeObjectLock&) = delete; + ThreadsafeObjectLock operator=(const ThreadsafeObjectLock&) = delete; + ~ThreadsafeObjectLock() { + _manager->_mutex.unlock(); + } + + T* operator->() { + return _manager->_obj.get(); + } + + friend ThreadsafeObject; + + protected: + ThreadsafeObjectLock( + ThreadsafeObject* const manager_ + ) : _manager(manager_) { + if (!_manager) { + using Codes = ObjectManagerError::Codes; + THROW(ObjectManagerError(Codes::NO_MANAGER)); + } + _manager->_mutex.lock(); + } + ThreadsafeObject* const _manager; +}; + +} // namespace nb +#endif // _NB_CORE_TYPES \ No newline at end of file diff --git a/engine/NBCore/src/Errors.cpp b/engine/NBCore/src/Errors.cpp deleted file mode 100644 index 544346a..0000000 --- a/engine/NBCore/src/Errors.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Errors.hpp" - -namespace nb { - -const std::string Error::type = "Error"; -const ErrorCodeMap Error::ErrorMessages = { - {ErrorCodes::GENERAL, "General std::exception."}, - {ErrorCodes::UNDEFINED, "Undefined / general error."}, - {ErrorCodes::BADERRORCODE, "Unrecognized error code."} -}; - -} diff --git a/engine/NBCore/src/ErrorsImpl.cpp b/engine/NBCore/src/ErrorsImpl.cpp new file mode 100644 index 0000000..a36b110 --- /dev/null +++ b/engine/NBCore/src/ErrorsImpl.cpp @@ -0,0 +1,16 @@ +#include "ErrorsImpl.hpp" + +namespace nb { + +const std::string Error<>::type = "nb::Error"; +const ErrorCodeMap Error<>::ErrorMessages = { + {Error::Codes::STANDARD, "std::exception"}, + {Error::Codes::UNDEFINED, "Error"} +}; + +//ErrorBase:: + +ErrorBase::ErrorBase(const std::exception& exception_) noexcept +: ErrorBase(Error(exception_)) {} + +} diff --git a/engine/NBCore/src/Logger.cpp b/engine/NBCore/src/Logger.cpp index 1deed87..70c8ff8 100644 --- a/engine/NBCore/src/Logger.cpp +++ b/engine/NBCore/src/Logger.cpp @@ -1,15 +1,38 @@ #include #include "Logger.hpp" +#include "StringUtils.hpp" namespace nb { +#ifndef _NB_NO_LOGGER nb::DefaultDebugLogger logger(std::cout); +#endif // _NB_NO_LOGGER + +bool nb::DefaultDebugLogger::process(const LogEvent& msg) { + constexpr size_t level_field_width = 5; + constexpr size_t msg_prompt_length = level_field_width+7; + for (const auto os : this->_ostream) { + *os << "["; + os->width(level_field_width); + *os << std::to_string(msg.lvl) << "] :: "; + std::string fileloc=""; + if (!msg.file.empty()) { + fileloc += "(" + msg.file + ":" + std::to_string(msg.line) + ") - "; + } + *os << indent_strblock( + fileloc + msg.msg, + std::string(20 - msg_prompt_length, ' '), + "" + ) << nb::NEWLINE; + } + return true; +} static bool RUN_LOGGER(nb::DefaultDebugLogger& log) { return log.run(); } -static const bool LOGGER_RUNNING = RUN_LOGGER(logger); +const bool LOGGER_RUNNING = RUN_LOGGER(logger); } // namespace nb \ No newline at end of file diff --git a/engine/NBCore/src/Processes.cpp b/engine/NBCore/src/Processes.cpp index dabf222..f0aec20 100644 --- a/engine/NBCore/src/Processes.cpp +++ b/engine/NBCore/src/Processes.cpp @@ -7,7 +7,6 @@ #endif // _NB_TARGET_LINUX #include "Processes.hpp" -#include "Types.hpp" namespace nb { @@ -16,8 +15,15 @@ uint64_t GetPID() { return GetCurrentProcessId(); } #endif // _NB_TARGET_WINDOWS - #ifdef _NB_TARGET_LINUX #endif // _NB_TARGET_LINUX +#ifdef _NB_TARGET_WINDOWS +uint64_t GetTID() { + return GetCurrentThreadId(); +} +#endif // _NB_TARGET_WINDOWS +#ifdef _NB_TARGET_LINUX +#endif // _NB_TARGET_LINUX + } // namespace nb \ No newline at end of file diff --git a/engine/NBCore/src/StringUtils.cpp b/engine/NBCore/src/StringUtils.cpp new file mode 100644 index 0000000..1946c3c --- /dev/null +++ b/engine/NBCore/src/StringUtils.cpp @@ -0,0 +1,29 @@ +#include "StringUtils.hpp" + +/* std::string nb::wstr_to_str(std::wstring in) { + std::size_t wstrlen = in.length(); + char* c_str= new char[wstrlen]; + std::wcstombs(c_str, in.c_str(), wstrlen); + std::string ret(c_str, wstrlen); + delete[] c_str; + return ret; +} */ + +std::string nb::indent_strblock( + std::string block, + std::string prepend, + std::string topIndent +) { + return topIndent + nb::find_and_replace( + block, + nb::NEWLINE, + nb::NEWLINE + prepend + ); +} + +std::string nb::indent_strblock( + std::string block, + std::string prepend +) { + return nb::indent_strblock(block, prepend, prepend); +} diff --git a/engine/NBCore/src/Utils.cpp b/engine/NBCore/src/Utils.cpp new file mode 100644 index 0000000..39fc680 --- /dev/null +++ b/engine/NBCore/src/Utils.cpp @@ -0,0 +1,27 @@ +#include "Utils.hpp" + +namespace nb { + +using ObjectManagerCodes = ObjectManagerError::Codes; +const std::string ObjectManagerError::type = "nb::ObjectManagerError"; +const ErrorCodeMap ObjectManagerError::ErrorMessages({ + {ObjectManagerCodes::UNDEFINED, "Error"}, + { + ObjectManagerCodes::NO_MANAGER, + "Attempting to create object lock without lock manager" + }, + { + ObjectManagerCodes::MANAGER_MISMATCH, + "Attempting to delete object lock from mismatched lock manager" + }, + { + ObjectManagerCodes::LOCK_OVERFLOW, + "Too many object locks allocated" + }, + { + ObjectManagerCodes::BAD_THREAD, + "Attempting operation from a bad thread" + } +}); + +}; // namespace nb diff --git a/engine/NBCore/tests/CMakeLists.txt b/engine/NBCore/tests/CMakeLists.txt index d3ad479..34476ff 100644 --- a/engine/NBCore/tests/CMakeLists.txt +++ b/engine/NBCore/tests/CMakeLists.txt @@ -4,12 +4,21 @@ if (NB_BUILD_TESTS) enable_testing() include(GoogleTest) add_executable(TestCore - testErrors.cpp - testProcesses.cpp + ./testErrors.cpp + #./testProcesses.cpp + ./testUtils.cpp ) target_link_libraries(TestCore NBCore GTest::gtest_main ) + + add_executable(LoggerTest + ./testLogger.cpp + ) + target_link_libraries(LoggerTest + NBCore + ) + gtest_discover_tests(TestCore) endif() \ No newline at end of file diff --git a/engine/NBCore/tests/testErrors.cpp b/engine/NBCore/tests/testErrors.cpp index 7d79ee9..0c7be49 100644 --- a/engine/NBCore/tests/testErrors.cpp +++ b/engine/NBCore/tests/testErrors.cpp @@ -1,38 +1,39 @@ #define CODE_ERROR_LOCATIONS -#include "Errors.hpp" #include -#include -#include -#include "Logger.hpp" + +#include "Errors.hpp" +#include using namespace nb; -class TestError : public ErrorBase { +class TestError : public Error { + using Base = Error; public: - using ErrorBase::ErrorBase; + using Base::Base; + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; - enum ErrorCodes : unsigned int { + enum Codes : unsigned int { A, B, C, D }; + static const std::string type; - static const ErrorCodeMap ErrorMessages; + static const nb::ErrorCodeMap ErrorMessages; }; const std::string TestError::type="TestError"; const ErrorCodeMap TestError::ErrorMessages{ - {TestError::ErrorCodes::A, "Hey!"}, - {TestError::ErrorCodes::B, "How"}, - {TestError::ErrorCodes::C, "You"}, - {TestError::ErrorCodes::D, "Doin"} + {TestError::Codes::A, "Hey!"}, + {TestError::Codes::B, "How"}, + {TestError::Codes::C, "You"}, + {TestError::Codes::D, "Doin"} }; TEST(ErrorTest, Test) { - EXPECT_EQ(1, 1); - - std::stringstream sstream; - - ASSERT_TRUE(nb::logger.isRunning()); - nb::logger.log("Hey!"); + auto err = TestError(0, TestError(1, TestError(2, TestError(3, TestError(2))))); + ASSERT_STREQ(err.what().c_str(), "Hey!\n Trace: How\n Trace: You\n Trace: Doin\n Trace: You"); } \ No newline at end of file diff --git a/engine/NBCore/tests/testLogger.cpp b/engine/NBCore/tests/testLogger.cpp new file mode 100644 index 0000000..27da8da --- /dev/null +++ b/engine/NBCore/tests/testLogger.cpp @@ -0,0 +1,46 @@ +#include + +#include "Errors.hpp" +#include "Logger.hpp" + +class TestError : public nb::Error { + using Base = Error; +public: + using Base::Base; + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; + + enum Codes : unsigned int { + A, B, C, D + }; + + static const std::string type; + static const nb::ErrorCodeMap ErrorMessages; +}; + +const std::string TestError::type="TestError"; +const nb::ErrorCodeMap TestError::ErrorMessages{ + {TestError::Codes::A, "Hey!"}, + {TestError::Codes::B, "How"}, + {TestError::Codes::C, "You"}, + {TestError::Codes::D, "Doin"} +}; + +int main() { + nb::logger.log("Whoop!"); + try { + THROW(TestError(0, TestError(1, TestError(2, TestError(3, TestError(2)))))); + } catch (TestError e){ + nb::logger.log("nb::Error was thrown!"); + } + + try { + THROW(std::exception()); + } catch (std::exception e){ + nb::logger.log("std::exception was thrown!"); + } + ERROR("hello there!"); + return 0; +} \ No newline at end of file diff --git a/engine/NBCore/tests/testProcesses.cpp b/engine/NBCore/tests/testProcesses.cpp index 3e220b8..cee1e90 100644 --- a/engine/NBCore/tests/testProcesses.cpp +++ b/engine/NBCore/tests/testProcesses.cpp @@ -1,8 +1,6 @@ #define CODE_ERROR_LOCATIONS #include - -#include #include "Processes.hpp" #include diff --git a/engine/NBCore/tests/testUtils.cpp b/engine/NBCore/tests/testUtils.cpp new file mode 100644 index 0000000..a726369 --- /dev/null +++ b/engine/NBCore/tests/testUtils.cpp @@ -0,0 +1,148 @@ +#define CODE_ERROR_LOCATIONS + +#include + +#include +#include "TypeTraits.hpp" +#include "StringUtils.hpp" + +namespace nb { + +TEST(UtilsTest, TestFindAndReplace) { + ASSERT_STREQ( + find_and_replace("Jeff", "e", "efe").c_str(), + "Jefeff" + ); + + std::string tmp = find_and_replace("Naif", "a", "afa"); + ASSERT_STREQ( + find_and_replace(tmp, "i", "ifi").c_str(), + "Nafaifif" + ); + + tmp = find_and_replace("aeiou", "a", "afa"); + tmp = find_and_replace(tmp, "e", "efe"); + tmp = find_and_replace(tmp, "i", "ifi"); + tmp = find_and_replace(tmp, "o", "ofo"); + tmp = find_and_replace(tmp, "u", "ufu"); + ASSERT_STREQ( + tmp.c_str(), + "afaefeifiofoufu" + ); + + tmp = find_and_replace(tmp, "afa", "a"); + tmp = find_and_replace(tmp, "efe", "e"); + tmp = find_and_replace(tmp, "ifi", "i"); + tmp = find_and_replace(tmp, "ofo", "o"); + tmp = find_and_replace(tmp, "ufu", "u"); + ASSERT_STREQ( + tmp.c_str(), + "aeiou" + ); +} + +TEST(UtilsTest, TestFindAndReplaceWstr) { + ASSERT_STREQ( + find_and_replace(L"Jeff", L"e", L"efe").c_str(), + L"Jefeff" + ); + + std::wstring tmp = find_and_replace(L"Naif", L"a", L"afa"); + ASSERT_STREQ( + find_and_replace(tmp, L"i", L"ifi").c_str(), + L"Nafaifif" + ); + + tmp = find_and_replace(L"aeiou", L"a", L"afa"); + tmp = find_and_replace(tmp, L"e", L"efe"); + tmp = find_and_replace(tmp, L"i", L"ifi"); + tmp = find_and_replace(tmp, L"o", L"ofo"); + tmp = find_and_replace(tmp, L"u", L"ufu"); + ASSERT_STREQ( + tmp.c_str(), + L"afaefeifiofoufu" + ); + + tmp = find_and_replace(tmp, L"afa", L"a"); + tmp = find_and_replace(tmp, L"efe", L"e"); + tmp = find_and_replace(tmp, L"ifi", L"i"); + tmp = find_and_replace(tmp, L"ofo", L"o"); + tmp = find_and_replace(tmp, L"ufu", L"u"); + ASSERT_STREQ( + tmp.c_str(), + L"aeiou" + ); +} + +/* TEST(UtilsTest, TestWstrToStr) { + ASSERT_STREQ( + nb::wstr_to_str(L"Hi!").c_str(), + "Hi!" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"Naif").c_str(), + "Naif" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"\r\r\r\n").c_str(), + "\r\r\r\n" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"Naif\ttalks\r\ra\t\nlot").c_str(), + "Naif\ttalks\r\ra\t\nlot" + ); +} */ + +/* TEST(UtilsTest, TestStrToWstr) { + ASSERT_STREQ( + nb::str_to_wstr("Hi!").c_str(), + L"Hi!" + ); + ASSERT_STREQ( + nb::str_to_wstr("Naif").c_str(), + L"Naif" + ); + ASSERT_STREQ( + nb::str_to_wstr("\r\r\r\n").c_str(), + L"\r\r\r\n" + ); + ASSERT_STREQ( + nb::str_to_wstr("Naif\ttalks\r\ra\t\nlot").c_str(), + L"Naif\ttalks\r\ra\t\nlot" + ); +} */ + +struct A { bool x(); }; +struct B { bool y(); }; +struct C { bool x(); bool y; }; +struct D : C {}; + +template +using has_x = decltype(T::x); + +template +using has_y = decltype(T::y); + +TEST(UtilsTest, TestIsDetected) { + auto ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_FALSE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_FALSE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_TRUE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_TRUE(ret); +} + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/Buffers.hpp b/engine/NBGraphics/Buffers.hpp index 58e8c42..8990d8f 100644 --- a/engine/NBGraphics/Buffers.hpp +++ b/engine/NBGraphics/Buffers.hpp @@ -4,362 +4,238 @@ #include "GLLoad.hpp" +#include #include -#include #include -#include +#include +#include + +#include "OGLObjects.hpp" namespace nb { - -typedef std::vector RawVec; -static uint8_t GLSLTypeSize(GLenum type) { - switch(type) { - case GL_SHORT: - case GL_UNSIGNED_SHORT: - case GL_HALF_FLOAT: - return 2; - break; - case GL_INT: - case GL_UNSIGNED_INT: - case GL_FLOAT: - return 4; - break; - case GL_DOUBLE: - return 8; - break; - default: - return 1; - break; - } -} +extern const std::unordered_map BufferTypes; -struct VertexAttributePointer { - GLuint buffer = 0; - int32_t offset = 0; - GLsizei stride = -1; - GLuint divisor = 0; -}; - -struct VertexAttribute { - GLint GLSLSize; - GLenum GLSLType; - GLboolean GLSLNormalization; - VertexAttributePointer ptr; -}; - -static bool isIndexInVertexAttribute(int i, const VertexAttribute& va) { - if ((i -= va.ptr.offset) < 0) { return false; } - return (i%va.ptr.stride) < GLSLTypeSize(va.GLSLType) * va.GLSLSize; -} - -typedef std::vector VertexAttributeList; - -template -RawVec vectorToRaw(const std::vector& vec) { - unsigned int num_bytes = vec.size() * sizeof(T); - RawVec ret(num_bytes); - memcpy(ret.data(), vec.data(), num_bytes); - return ret; -} - -template -RawVec concatVectorsToRaw(const std::vector& vec1, const std::vector& vec2) { - RawVec vec1_raw = vectorToRaw(vec1); - RawVec vec2_raw = vectorToRaw(vec2); - unsigned int vec1_raw_size = vec1_raw.size(); - unsigned int vec2_raw_size = vec2_raw.size(); - RawVec ret(vec1_raw_size + vec2_raw_size); - memcpy(ret.data(), vec1_raw.data(), vec1_raw_size); - memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size); - return ret; -} - -template -std::vector rawToVector(const RawVec& vec) { - if (vec.size() % sizeof(T) != 0) { - throw std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">."); - } - unsigned int num_elmts = vec.size() / sizeof(T); - std::vector ret(num_elmts); - memcpy(ret.data(), vec.data(), vec.size()); - - return ret; -} - -class BufferError : public std::runtime_error { -public: - const bool error; - BufferError(const std::string& msg, const std::string& file="", int line=-1) - : std::runtime_error(NB::formatDebugString(msg, file, line)), error(true) {} - BufferError(bool isError=true) : std::runtime_error(""), error(isError) {} -}; - - -template -class OpenGLObject { -public: - OpenGLObject(OpenGLObject&&) = default; - OpenGLObject& operator=(OpenGLObject&&) = default; - ~OpenGLObject() { remove(); } - - virtual void bind() const = 0; - virtual void unbind() const = 0; - virtual bool isInitialized() const = 0; - GLuint id() const { return _id; } - -protected: - OpenGLObject() = default; - OpenGLObject(const OpenGLObject&) = delete; - OpenGLObject& operator=(const OpenGLObject&) = delete; - - virtual void remove() const = 0; - - GLuint _id; -}; - -class VAO : public virtual OpenGLObject { -public: - using Base = OpenGLObject; +class BufferError : public Error { + protected: + using Base = Error; using Base::Base; - VAO() { glGenVertexArrays(1, &_id); } - - void bind() const { glBindVertexArray(_id); } - void unbind() const { glBindVertexArray(0); } - - -protected: - using Base::_id; - const - - void remove() { glDeleteVertexArrays(1, &_id); } - + public: + enum Codes : unsigned int { + UNDEFINED, + DATA_OVERFLOW, + INVALID_BUFFER, + HANDLE_OVERWRITE, + }; + static const std::string type; + static const ErrorCodeMap ErrorMessages; }; template -class Buffer : public virtual OpenGLObject> { -public: - using Base = OpenGLObject; - using Base::Base; +class Buffer : public OpenGLObject { + using Codes = BufferError::Codes; - Buffer(GLenum usage_ = GL_STATIC_DRAW) : usage(usage_) { glGenBuffers(1, &_id); } + public: + using OpenGLObject::OpenGLObject; + Buffer() = default; + Buffer(Buffer&& other) { + *this = std::move(other); + } + Buffer& operator=(Buffer&& rhs) { + auto targ_name = BufferTypes.at(Target); + if (Target != rhs.Target) { + THROW(BufferError( + Codes::INVALID_BUFFER, + "w/ ("+targ_name+") := ("+BufferTypes.at(rhs.Target)+")" + )); + } + //nb::logger.log(std::to_string(_id) + ":=" + std::to_string(rhs._id)); + if (_id) { + THROW(BufferError( + Codes::HANDLE_OVERWRITE, + "w/ BufferType = " + targ_name + )); + } + _id = rhs._id; + _usage = rhs._usage; + _size = rhs._size; - void bind() const { glBindBuffer(GLTarget, _id); } - void unbind() const { glBindBuffer(GLTarget, 0); } - void data(void* data_, size_t size) const { glBufferData(GLTarget, size, data_, usage); } - void data(nb::ByteVector& data_) const { glBufferData(GLTarget, data_.size(), data_.data(), usage); } - void invalidate() const { glInvalidateBufferData(_id); } + rhs._id = 0; + rhs._usage = GL_STATIC_DRAW; + rhs._size = 0; + return *this; + } + virtual void bind() const override { + if (_id) { + glBindBuffer(Target, _id); + } else { + WARN(BufferError( + Codes::INVALID_BUFFER, + "w/ BufferType " + BufferTypes.at(Target) + ), 0xFE); + } + } + virtual void unbind() const override { glBindBuffer(Target, 0); } + virtual GLenum usage() const { return _usage; } + virtual GLenum usage(GLenum usage_) { + _usage = usage_; + data(data(), _usage); + return _usage; + } + virtual size_t size() const { return _size; } + virtual ByteVector data() const { + bind(); + ByteVector ret(_size); + glGetBufferSubData(Target, 0, _size, ret.data()); + return ret; + } + virtual void data( + const void* data_, + GLsizei size_, + GLenum usage_=GL_STATIC_DRAW + ) { + declare(); + bind(); + glBufferData(Target, size_, data_, usage_); + _size = size_; + _usage = usage_; + } + virtual void data(const ByteVector& data_, GLenum usage_=GL_STATIC_DRAW) { + data(data_.data(), data_.size(), usage_); - const GLenum GLTarget = BufferType::GLTarget; - GLenum usage; + } + virtual void subdata(void* data_, GLsizeiptr size_, GLintptr offset_=0) { + bind(); + if (offset_+size_ <= _size) { + THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); + } + glBufferSubData(Target, offset_, size_, data_); + } + virtual void subdata(ByteVector& data_, GLintptr offset_=0) { + bind(); + size_t size_ = data_.size(); + if (offset_+size_ <= _size) { + THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); + } + glBufferSubData(Target, offset_, data_.size(), data_.data()); + } + // virtual void clear() const { glClearBufferData(Target, ) } + static const GLenum Target; + + protected: + using OpenGLObject::_id; + GLenum _usage = GL_STATIC_DRAW; + size_t _size; + virtual GLuint declare() override { + if (!_id) { + glGenBuffers(1, &_id); + } + return _id; + } + virtual void remove() override { + if (_id) { + glDeleteBuffers(1, &_id); + _id = 0; + } + } + +}; + +template +const GLenum Buffer::Target = BufferType::Target; + +template +class ImmutableBuffer : public virtual Buffer { + public: + using Base = Buffer; + using Base::Target; + using Base::bind; + + ImmutableBuffer() = default; + ImmutableBuffer(size_t size_, GLenum usage_ = GL_STATIC_DRAW, GLbitfield flags_=0x0) : size(size_), Base::usage(usage_) { + glBufferStorage(Target, size, nullptr, flags_); + } + + std::weak_ptr map(GLbitfield access_) { + if (_map && _mapAccess != access_) { unmap(); } + _mapAccess = access_; + if (!_map) { + bind(); + glMapBuffer(Target, _mapAccess); + } + return _map; + } + void unmap() { + bind(); + glUnmapBuffer(Target); + _map.reset(); + } + + const size_t size; protected: using Base::_id; - void remove() { glDeleteBuffers(1, &_id); } + std::shared_ptr _map = nullptr; + GLbitfield _mapAccess = 0; }; -class VertexBuffer : public virtual Buffer { -public: +template +class ArrayBuffer : public Buffer> { + using Base = Buffer>; + using BufferType = Base; - static const GLenum GLTarget = GL_ARRAY_BUFFER; -protected: + public: + using Base::Base; + using Base::data; + ArrayBuffer(const ByteVector&, GLenum usage_=GL_STATIC_DRAW); + + static const GLenum Target = GL_ARRAY_BUFFER; + + protected: + using Base::_usage; +}; + +template <> +class ArrayBuffer +: public virtual ArrayBuffer, public virtual ImmutableBuffer> { + using Base = ArrayBuffer; + using BufferType = ImmutableBuffer>; + using BufferType::BufferType; + + + public: + using Base::Target; }; -class ElementBuffer : public virtual Buffer { -public: +template +class ElementBuffer : public Buffer> { + using Base = Buffer>; + using BufferType = Base; + + public: + using Base::Base; + static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER; - static const GLenum GLTarget = GL_ELEMENT_ARRAY_BUFFER; - -protected: + protected: }; -/* -class Buffer : public OpenGLObject { -public: - Buffer( - GLenum buffer_type, - GLenum usage=GL_STATIC_DRAW - ) : _type(buffer_type), _usage(usage) { - glGenBuffers(1, &_id); - } - Buffer( - const RawVec& init_data, - GLenum buffer_type, - GLenum usage=GL_STATIC_DRAW - ) : _type{buffer_type}, _usage{usage} { data(init_data); } - Buffer( - unsigned int size, - GLenum buffer_type, - GLenum usage=GL_STATIC_DRAW - ) : Buffer(RawVec(size), buffer_type, usage) {} +template <> +class ElementBuffer +: public virtual ElementBuffer, public virtual ImmutableBuffer> { + using Base = ElementBuffer; + using BufferType = ImmutableBuffer>; - Buffer(Buffer&& rhs) { *this = std::move(rhs); } - Buffer& operator=(Buffer&& rhs) { - remove(); - _usage = rhs._usage; - _id = rhs._id; - rhs._id = 0; - return *this; - } - - GLenum usage() const { return _usage; } - unsigned int id() const { return _id; } - GLuint size() const { return _size; } - bool isInitialized() const override { return _id && glIsBuffer(_id) && bool(_size); } - RawVec data() const { - RawVec ret(_size); - bind(); - glGetBufferSubData(_type, 0, _size, ret.data()); - return ret; - } - void bind() const override { glBindBuffer(_type, _id); } - void unbind() const override { glBindBuffer(_type, 0); } - - GLenum usage(GLenum usage) { - _usage=usage; - data(data()); - return _usage; - } - void data(const RawVec& set_data) { - if (!glfwGetCurrentContext()) { - THROW_BUFFER_ERROR("No OpenGL context."); - } - bind(); - glBufferData(_type, set_data.size(), set_data.data(), _usage); - _size = set_data.size(); - } - void data(const RawVec& newData, unsigned int offset) { - if (newData.size()+offset > _size) { - THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + "."); - } - bind(); - glBufferSubData(_type, offset, newData.size(), newData.data()); - } - void data(void* src, unsigned int offset, unsigned int size) { - if (size+offset > _size) { - THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + "."); - } - bind(); - glBufferSubData(_type, offset, size, src); - } - -protected: - virtual void remove() const override { - unbind(); - if (_id) { glDeleteBuffers(1, &_id); } - } - - using OpenGLObject::_id; - GLenum _type; - GLenum _usage; - unsigned int _size = 0; -}; - - -class Texture : public OpenGLObject { -public: - Texture(GLenum tar=GL_TEXTURE_2D) : _target(tar) { glGenTextures(1, &_id); } - - Texture(Texture&& rhs) { *this=std::move(rhs); } - Texture& operator=(Texture&& rhs) { - _target = rhs._target; - _id = rhs._id; - rhs._id = 0; - return *this; - } - - void bind() const override { glBindTexture(_target, _id); } - void unbind() const override { glBindTexture(_target, 0); } - bool isInitialized() const override { return _id && glIsTexture(_id); } - -protected: - using OpenGLObject::_id; - GLenum _target; -}; - -class Framebuffer : public OpenGLObject { -public: - - Framebuffer(GLenum tar=GL_FRAMEBUFFER) : _target(tar) { glGenFramebuffers(1, &_id); } - - Framebuffer(Framebuffer&& rhs) { *this=std::move(rhs); } - Framebuffer& operator=(Framebuffer&& rhs) { - remove(); - _target = rhs._target; - _id = rhs._id; - rhs._id = 0; - return *this; - } - - void bind() const override { glBindFramebuffer(_target, _id); } - void unbind() const override { glBindFramebuffer(_target, 0); } - bool isInitialized() const override { return _id && glIsFramebuffer(_id); } - GLenum status() const { - bind(); - return glCheckFramebufferStatus(_target); - } - - -protected: - virtual void remove() const override { - unbind(); - if (_id) { glDeleteFramebuffers(1, &_id); } - } - using OpenGLObject::_id; - GLenum _target; -}; - -class Renderbuffer : public OpenGLObject { -public: - Renderbuffer(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) : Renderbuffer() { - storage(x, y, format, multi); - } - Renderbuffer() { glGenRenderbuffers(1, &_id); } - - - Renderbuffer(Renderbuffer&& rhs) { *this = std::move(rhs); } - Renderbuffer& operator=(Renderbuffer&& rhs) { - _format = rhs._format; - _id = rhs._id; - rhs._id = 0; - return *this; - } - - virtual void bind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); } - virtual void unbind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); } - virtual bool isInitialized() const override { return _id && glIsRenderbuffer(_id); } - - void storage(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) { - _sizex = x; - _sizey = y; - _format = format; - _multisample = multi; - bind(); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, _multisample, _format, _sizex, _sizey); - } - - -protected: - virtual void remove() const override { - unbind(); - if (_id) { glDeleteRenderbuffers(1, &_id); } - } - - using OpenGLObject::_id; - GLenum _format; - unsigned int _sizex{0}; - unsigned int _sizey{0}; - unsigned int _multisample{0}; + public: + using BufferType::BufferType; + using Base::Target; }; -*/ - } // namespace NB #endif // _NB_BUFFER diff --git a/engine/NBGraphics/CMakeLists.txt b/engine/NBGraphics/CMakeLists.txt index 6a22f25..5a94b3f 100644 --- a/engine/NBGraphics/CMakeLists.txt +++ b/engine/NBGraphics/CMakeLists.txt @@ -1,5 +1,3 @@ -include_directories(./.) - find_package(OpenGL) add_subdirectory(${GLFW_PATH} ${GLFW_PATH}/build) @@ -10,8 +8,12 @@ set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) toAbsolutePath(NB_GRAPHICS_SOURCE - ./src/Window.cpp ./src/Buffers.cpp + ./src/OGLObjects.cpp + ./src/ProgramPipeline.cpp + ./src/Textures.cpp + ./src/VertexArray.cpp + ./src/Window.cpp ) toAbsolutePath(NB_GRAPHICS_INCLUDE @@ -19,8 +21,10 @@ toAbsolutePath(NB_GRAPHICS_INCLUDE ./Camera.hpp ./Draw.hpp ./GLLoad.hpp - ./shader.hpp - ./VAOManager.hpp + ./OGLObjects.hpp + ./ProgramPipeline.hpp + ./Textures.hpp + ./VertexArray.hpp ./Window.hpp ) @@ -31,4 +35,11 @@ add_library(NBGraphics ${NB_GRAPHICS_SOURCE} ${GLAD_PATH}/src/glad.c ) -target_link_libraries(NBGraphics glfw3) +target_link_libraries(NBGraphics glfw) + +target_include_directories(NBGraphics PUBLIC ./.) + +if (NB_BUILD_TESTS) + add_subdirectory(./tests) +endif() + diff --git a/engine/NBGraphics/Camera.hpp b/engine/NBGraphics/Camera.hpp index 35e109d..8e31c8c 100644 --- a/engine/NBGraphics/Camera.hpp +++ b/engine/NBGraphics/Camera.hpp @@ -2,13 +2,14 @@ #ifndef _NB_CAMERA #define _NB_CAMERA -#include +#include "GLLoad.hpp" + #include #include #include #include -namespace NB { +namespace nb { class Camera { public: diff --git a/engine/NBGraphics/GLLoad.hpp b/engine/NBGraphics/GLLoad.hpp index 5936ed9..8158ecd 100644 --- a/engine/NBGraphics/GLLoad.hpp +++ b/engine/NBGraphics/GLLoad.hpp @@ -5,31 +5,4 @@ #include #include -#include -#include - -namespace NB { - -static unsigned int __NB_GL_DEBUG_ERROR_CODE__; - -static std::string formatDebugString(const std::string& msg, const std::string& file="", int line=-1) { - std::string ret = ""; - if (file != "") { - ret += "In file " + file; - if (line >= 0) { - ret += " at line " + std::to_string(line); - } - ret += ":\n\t"; - } - return ret + msg; -} - -} - -#define NB_GL_DEBUG_THROW(cmd, when) while(NB::__NB_GL_DEBUG_ERROR_CODE__=glGetError()){\ - std::cout << "[GL ERROR]: " << NB::__NB_GL_DEBUG_ERROR_CODE__;\ - std::cout << " " << when << " " << cmd << "\n";\ -} -#define NB_GL_DEBUG(cmd) NB_GL_DEBUG_THROW(#cmd, "before"); cmd; NB_GL_DEBUG_THROW(#cmd, "after"); - #endif \ No newline at end of file diff --git a/engine/NBGraphics/OGLObjects.hpp b/engine/NBGraphics/OGLObjects.hpp new file mode 100644 index 0000000..bb0dc8a --- /dev/null +++ b/engine/NBGraphics/OGLObjects.hpp @@ -0,0 +1,45 @@ +#pragma once +#ifndef _NB_OGL_OBJECTS +#define _NB_OGL_OBJECTS + +#include "GLLoad.hpp" + +#include + +namespace nb { + +class OGLError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, HANGING_OBJECT + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class OpenGLObject { + public: + OpenGLObject(const OpenGLObject&) = delete; + OpenGLObject& operator=(const OpenGLObject&) = delete; + virtual ~OpenGLObject() {}; + + virtual void bind() const = 0; + virtual void unbind() const = 0; + GLuint id() const { return _id; } + + protected: + OpenGLObject() = default; + + virtual GLuint declare() = 0; + virtual void remove() = 0; + + GLuint _id = 0; +}; + +} // namespace nb +#endif // _NB_OGL_OBJECTS \ No newline at end of file diff --git a/engine/NBGraphics/ProgramPipeline.hpp b/engine/NBGraphics/ProgramPipeline.hpp new file mode 100644 index 0000000..fce51a7 --- /dev/null +++ b/engine/NBGraphics/ProgramPipeline.hpp @@ -0,0 +1,123 @@ +#pragma once +#ifndef _NB_SHADER +#define _NB_SHADER + +#include "GLLoad.hpp" + +#include + +#include +#include + +#include "OGLObjects.hpp" + +namespace nb { + +class ShaderError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class ProgramError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, LINKING_ERROR + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +enum OpenGLProfiles { + Core, + Compatibility, + ES +}; + +class Shader : public OpenGLObject { + using Base = OpenGLObject; + + public: + using Base::Base; + using Base::id; + Shader(GLenum, const std::string&); + Shader(Shader&&); + Shader& operator=(Shader&&) = delete; + ~Shader() { remove(); } + operator bool(); + virtual void bind() const override { /* TODO: Some warning of some kind perhaps*/ } + virtual void unbind() const override { /* TODO: Some warning of some kind perhaps*/ } + GLint status(GLenum) const; + std::string log() const; + const GLenum target; + friend Base; + + protected: + using Base::_id; + GLint _success; + virtual GLuint declare() override { + if (!_id) { + _id = _id = glCreateShader(target); + } + return _id; + } + + virtual void remove() override { + if (_id) { + glDeleteShader(_id); + } + } + +}; + +class Program : public OpenGLObject { + using Base = OpenGLObject; + + public: + using Base::Base; + using Base::id; + Program(SharedVector); + Program(Program&&); + Program& operator=(Program&&) = delete; + operator bool(); + ~Program() { remove(); } + virtual void bind() const override { + glUseProgram(_id); + } + virtual void unbind() const override { /* TODO: Some warning of some kind perhaps*/ } + GLint status(GLenum) const; + std::string log() const; + friend Base; + + protected: + using Base::_id; + GLint _success; + virtual GLuint declare() override { + if (!_id) { + _id = glCreateProgram(); + } + return _id; + } + virtual void remove() override { + if (_id) { + glDeleteProgram(_id); + } + } + +}; + +} +#endif \ No newline at end of file diff --git a/engine/NBGraphics/Textures.hpp b/engine/NBGraphics/Textures.hpp new file mode 100644 index 0000000..8984e72 --- /dev/null +++ b/engine/NBGraphics/Textures.hpp @@ -0,0 +1,61 @@ +#pragma once +#ifndef _NB_TEXTURES +#define _NB_TEXTURES + +#include "Buffers.hpp" +#include "OGLObjects.hpp" + +namespace nb { + +template +class TextureBuffer : public virtual Buffer> { + using Base = Buffer>; + using BufferType = Base; + + public: + using Base::Base; + static const GLenum Target = GL_TEXTURE_BUFFER; + + protected: + +}; + +template <> +class TextureBuffer +: public virtual TextureBuffer, public virtual ImmutableBuffer> { + using Base = TextureBuffer; + using BufferType = ImmutableBuffer>; + + public: + using BufferType::BufferType; + using Base::Target; +}; + +class Texture : public OpenGLObject { + using Base = OpenGLObject; +public: + using Base::Base; + using Base::id; + + Texture(GLenum); + + virtual void bind() const override { glBindTexture(target, _id); } + virtual void unbind() const override { glBindTexture(target, 0); } + + const GLenum target; + + friend Base; + + protected: + using Base::_id; + + virtual void remove() override { + if (_id) { + glDeleteTextures(0, &_id); + } + } + +}; + +} // namespace nb +#endif // _NB_TEXTURES \ No newline at end of file diff --git a/engine/NBGraphics/VAOManager.hpp b/engine/NBGraphics/VAOManager.hpp deleted file mode 100644 index 142882c..0000000 --- a/engine/NBGraphics/VAOManager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#ifndef _NB_VAO_MANAGER -#define _NB_VAO_MANAGER - -#include - -#include -#include -#include - -#include "Buffers.h" - -#define THROW_VAO_ERROR(msg) throw VAOError(msg, __FILE__, __LINE__); - -namespace NB { - -class VAOError : public std::runtime_error { -public: - const bool error; - VAOError(const std::string& msg, const std::string& file="", int line=-1) - : std::runtime_error(NB::formatDebugString(msg, file, line)), error(true) {} - VAOError(bool isError=true) : std::runtime_error(""), error(isError) {} -}; - -class VAOManager : public OpenGLObject { -public: - typedef std::shared_ptr BufferManagerPointer; - //typedef BufferManager BufferManagerPointer; - - VAOManager(); - VAOManager( - std::vector, - BufferManagerPointer elmt_buf = nullptr, - const VertexAttributeList& vert_attrs = {} - ); - VAOManager( - BufferManagerPointer vert_bufs, - BufferManagerPointer elmt_buf = nullptr, - const VertexAttributeList& vert_attrs = {} - ) : VAOManager(std::vector(1, vert_bufs), elmt_buf, vert_attrs) {} - - VAOManager(VAOManager&& rhs); - VAOManager& operator=(VAOManager&& rhs); - - VertexAttributeList getLayout() const; - GLuint id() const; - unsigned int vertSize(GLuint) const; - std::vector getVertexBuffers() const; - bool isInitialized() const override; - RawVec attributeData(unsigned int); - - RawVec attributeData(unsigned int, const RawVec&); - void bind() const; - void unbind() const; - void addVBO(BufferManagerPointer); - void changeEBO(BufferManagerPointer); - VertexAttributeList addVertexAttributes(const VertexAttributeList&); - VertexAttributeList addVertexAttributes(VertexAttribute); - VertexAttributeList generate(); - VertexAttributeList generate(VertexAttributeList); - VertexAttributeList changeLayout(unsigned int, VertexAttributePointer); - - VAOError checkValid(const VertexAttributeList&); - -private: - void remove() const override; - - using OpenGLObject::_id; - std::map _vert_buffers; - BufferManagerPointer _elmt_buffer = nullptr; - VertexAttributeList _vert_attrs; -}; - -} // namespace NB -#endif // _NB_VAO_MANAGER \ No newline at end of file diff --git a/engine/NBGraphics/VertexArray.hpp b/engine/NBGraphics/VertexArray.hpp new file mode 100644 index 0000000..2f97c28 --- /dev/null +++ b/engine/NBGraphics/VertexArray.hpp @@ -0,0 +1,177 @@ +#pragma once +#ifndef _NB_VERTEX_ARRAY +#define _NB_VERTEX_ARRAY + +#include "GLLoad.hpp" + +#include + +#include "Buffers.hpp" +#include "OGLObjects.hpp" + + +namespace nb { + +static uint8_t GLSLTypeSize(GLenum type) { + switch(type) { + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + return 4; + break; + case GL_DOUBLE: + return 8; + break; + default: + return 1; + break; + } +} + +struct VertexAttributeLayout { + int32_t offset = 0; + GLsizei stride = 0; + GLuint divisor = 0; +}; + +struct VertexAttribute { + GLint GLSLSize; + GLenum GLSLType; + GLboolean GLSLNormalization; + VertexAttributeLayout layout; +}; + +struct VertexAttributePointer { + VertexAttribute attribute; + GLuint buffer; + GLuint index; +}; + +static bool isIndexInVertexAttribute(int i, const VertexAttribute& va) { + if ((i -= va.layout.offset) < 0) { return false; } + return (i%va.layout.stride) < GLSLTypeSize(va.GLSLType) * va.GLSLSize; +} + +typedef std::vector VertexAttributeList; +typedef std::vector VertexAttributePointerList; + +class VAOError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, INVALID_ATTRIBUTE, INVALID_VAO + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class VAO : public OpenGLObject { + using Base = OpenGLObject; + using Codes = VAOError::Codes; + + public: + using Base::Base; + VAO() = default; + VAO(const VertexAttributePointerList&); + VAO(VAO&&); + + VAO& operator=(VAO&&); + + virtual void bind() const override { + if(_id) { + glBindVertexArray(_id); + } else { + THROW(VAOError(Codes::INVALID_VAO)); + } + } + virtual void unbind() const override { glBindVertexArray(0); } + + VertexAttributePointerList attributes() const; + VertexAttributePointerList attributes(const VertexAttributePointerList&); + VertexAttributePointer attr(GLuint) const; + void enable() const; + void enable(GLuint) const; + void disable() const; + void disable(GLuint) const; + + protected: + using Base::_id; + VertexAttributePointerList _attrs; + + virtual void remove() override { + if(_id) { + disable(); + bind(); + glDeleteVertexArrays(1, &_id); + _id = 0; + } + } + virtual GLuint declare() override { + if (!_id) { + glGenVertexArrays(1, &_id); + } + return _id; + } + +}; + +using VBO = ArrayBuffer; +using VertBufVec = SharedVector; +using EBO = ElementBuffer; + +struct VertexData { + std::shared_ptr vbo; + VertexAttributeList attrs; +}; + +using VertexDataVec = std::vector; + +class VertexGroup { + public: + VertexGroup(VertexGroup&&); + VertexGroup& operator=(VertexGroup&&); + + VertexGroup(const VertexData&); + VertexGroup(const VertexDataVec&); + VertexGroup(const ByteVector&, const VertexAttributeList&); + + void bind() const { + _ebo->bind(); + _vao->bind(); + } + void unbind() const { + _vao->unbind(); + _ebo->unbind(); + } + + VertexDataVec getBuffers(); + VertexData getBuffers(size_t); + size_t setBuffers(const VertexDataVec&); + size_t addBuffer(const VertexData&); + VertexData dropBuffer(size_t); + + VertexAttributePointer attribute(size_t) const; + void enableAttr(GLuint); + void disableAttr(GLuint); + void enableBuffer(size_t); + void disableBuffer(size_t); + void enable(); + void disable(); + + protected: + VertexDataVec _vertex_data; + std::shared_ptr _ebo; + std::shared_ptr _vao; +}; + +} // namespace nb +#endif // _NB_VERTEX_ARRAY \ No newline at end of file diff --git a/engine/NBGraphics/Window.hpp b/engine/NBGraphics/Window.hpp index 22a82f8..b72cfa6 100644 --- a/engine/NBGraphics/Window.hpp +++ b/engine/NBGraphics/Window.hpp @@ -4,22 +4,42 @@ #include "GLLoad.hpp" -#include #include #include #include #include +#include +#include + namespace nb { -class GLError : public std::runtime_error { -public: - GLError(const std::string&); +class OpenGLError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, INIT_FAILED, GLFW_INTIALIZED, GLAD_FAILED + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; }; -class WindowError : public std::runtime_error { -public: - WindowError(const std::string&); +class WindowError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, INITIALIZED_WINDOW, NO_GLFW, INIT_FAILED + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; }; class Window { diff --git a/engine/NBGraphics/shader.hpp b/engine/NBGraphics/shader.hpp deleted file mode 100644 index ca92fe3..0000000 --- a/engine/NBGraphics/shader.hpp +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once -#ifndef _NB_SHADER -#define _NB_SHADER - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// #define _PREPROC_FUNC_PARAMS_ (const ShaderPreprocessor&, ShaderUnit&, unsigned int, const std::string&, std::vector&) - -namespace NB { - -class ShaderPreprocessorError : public std::runtime_error { -public: - enum Codes : unsigned char { - NONE, - FILE_NOT_FOUND, - BUILTIN_NOT_FOUND, - CUSTOM, - UNDEFINED - }; - - const Codes code; - - ShaderPreprocessorError(const std::string&, const std::string& file="", int line=-1); - ShaderPreprocessorError(Codes, const std::string& arg="", const std::string& file="", int line=-1); - -protected: - static std::string errorCodeParser(Codes, const std::string& arg=""); -}; - -class ShaderError : public std::runtime_error { -public: - ShaderError(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); - -protected: - std::string formatString(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); -}; - -struct File { - // typedef std::tuple FilePath; - struct FilePath { - std::string dir; - std::string basename; - std::string ext; - }; - FilePath path; - std::stringstream src; - std::map> include_map; - - File() {} - File(const File& rhs) { *this = rhs; } - - File& operator=(const File& rhs) { - path = rhs.path; - src.str(""); - src << rhs.src.rdbuf(); - include_map = rhs.include_map; - return *this; - } -}; - -// File::FilePath get_file_path(std::string); - -class ShaderPreprocessor; - -enum OpenGLProfiles { - Core, - Compatibility, - ES -}; - -struct ShaderUnit { - GLenum type = 0x0; - File file; - std::string preprocSource; - std::map defines; - short vMajor=0, vMinor=0; - OpenGLProfiles profile = Core; -}; - -class ShaderPreprocessor; - -class ShaderProgram { - friend ShaderPreprocessor; -public: - ShaderProgram() {} - ShaderProgram(const ShaderProgram& rhs) = delete; - ShaderProgram(ShaderProgram&& rhs); - - ~ShaderProgram(); - - ShaderProgram& operator=(const ShaderProgram& rhs) = delete; - ShaderProgram& operator=(ShaderProgram&& rhs); - - std::vector getShaders() const; - ShaderUnit getShaders(unsigned int) const; - void use() const; - unsigned int id() const; - - void setBool(const std::string& name, bool value) const; - void setInt(const std::string& name, int value) const; - void setUnsigned(const std::string& name, int value) const; - void setFloat(const std::string& name, float value) const; - void setMat4(const std::string& name, glm::mat4& value) const; - - static ShaderProgram CreateShaderProgram(std::vector&); - -private: - ShaderProgram(std::vector& shaders) : _shader_units(shaders){} - - std::vector _shader_units; - unsigned int _id; -}; - -class ShaderPreprocessor { -public: - typedef ShaderPreprocessorError::Codes Codes; - - enum TokenType { - TK, - DR, - WS, - NL, - LC, - BC - }; - static std::string TokenName(const TokenType&); - - std::map AcceptedExtensions = { - {".frag", GL_FRAGMENT_SHADER}, - {".fs", GL_FRAGMENT_SHADER}, - {".vert", GL_VERTEX_SHADER}, - {".vs", GL_VERTEX_SHADER}, - {".tess", 0x0}, - {".geom", GL_GEOMETRY_SHADER}, - {".comp", 0x0}, - {".shad", 0x0}, - {".glsl", 0x0} - }; - std::map AcceptedProfiles { - {"core", Core}, - {"compatibility", Compatibility}, - {"es", ES} - }; - std::vector Directories; - std::map BuiltIns; - - ShaderPreprocessor(); - - - File load(const std::string, bool builtin_first=false) const; - ShaderUnit& preprocess(const std::string&, ShaderUnit&) const; - ShaderUnit preprocess(File, GLenum shader_type=0x0) const; - ShaderUnit preprocess(const std::string&, GLenum shader_type=0x0) const; - ShaderProgram ReloadFromFile(const ShaderProgram& rhs) const; - ShaderProgram CreateShaderProgram(std::vector) const; - -private: - typedef std::pair Token; - typedef std::vector StringVec; - - inline bool directive_dispatch(ShaderUnit&, const std::string&) const; - inline std::vector tokenize(const std::string&) const; - - inline bool preprocessor_include(ShaderUnit&, const StringVec&, const std::string&) const; - inline bool preprocessor_version(ShaderUnit&, const StringVec&, const std::string&) const; - inline bool preprocessor_define(ShaderUnit&, const StringVec&, const std::string&) const; - // inline bool preprocessor_uniform(ShaderUnit&, const std::string&, const std::string&) const; - - File loadFromBase(const std::string, const std::string base="") const; - File loadFromDirectories(const std::string) const; - File loadFromBuiltIn(const std::string) const; - File load_BuiltInFirst(const std::string) const; - File load_FilesFirst(const std::string) const; -}; - -} -#endif \ No newline at end of file diff --git a/engine/NBGraphics/src/Buffers.cpp b/engine/NBGraphics/src/Buffers.cpp index 24f9115..99379da 100644 --- a/engine/NBGraphics/src/Buffers.cpp +++ b/engine/NBGraphics/src/Buffers.cpp @@ -2,4 +2,25 @@ namespace nb { +const std::unordered_map BufferTypes({ + {GL_ARRAY_BUFFER, "GL_ARRAY_BUFFER"}, + {GL_ELEMENT_ARRAY_BUFFER, "GL_ELEMENT_BUFFER"} +}); + +using BufferErrorCodes = BufferError::Codes; +const std::string BufferError::type = "nb::BufferError"; +const ErrorCodeMap BufferError::ErrorMessages = { + { BufferErrorCodes::UNDEFINED, "Error" }, + { BufferErrorCodes::DATA_OVERFLOW, "Attempting buffer overflow" }, + { BufferErrorCodes::INVALID_BUFFER, "Attempting operation on invalid buffer" }, + { BufferErrorCodes::HANDLE_OVERWRITE, "Attempting to overwrite active buffer"} + +}; + +template<> +ArrayBuffer::ArrayBuffer(const ByteVector& data_, GLenum usage_) +: BufferType() { + data(data_, usage_); +} + } \ No newline at end of file diff --git a/engine/NBGraphics/src/Camera.cpp b/engine/NBGraphics/src/Camera.cpp index 8431597..e7aa711 100644 --- a/engine/NBGraphics/src/Camera.cpp +++ b/engine/NBGraphics/src/Camera.cpp @@ -1,5 +1,6 @@ -#include "Camera.h" -namespace NB { +#include "Camera.hpp" + +namespace nb { // Camera class Camera::Camera(const Vec3& pos, const Vec3& tar, const Vec3& up) { diff --git a/engine/NBGraphics/src/OGLObjects.cpp b/engine/NBGraphics/src/OGLObjects.cpp new file mode 100644 index 0000000..d66a2dc --- /dev/null +++ b/engine/NBGraphics/src/OGLObjects.cpp @@ -0,0 +1,11 @@ +#include "OGLObjects.hpp" + +namespace nb { + +const std::string OGLError::type = "nb::OGLError"; +const ErrorCodeMap OGLError::ErrorMessages = { + { OGLError::Codes::UNDEFINED, "Error" }, + {OGLError::Codes::HANGING_OBJECT, "Attempting to leave a hanging OpenGL object"} +}; + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/ProgramPipeline.cpp b/engine/NBGraphics/src/ProgramPipeline.cpp new file mode 100644 index 0000000..e45b65a --- /dev/null +++ b/engine/NBGraphics/src/ProgramPipeline.cpp @@ -0,0 +1,81 @@ +#include "ProgramPipeline.hpp" + +namespace nb{ + +using ShaderErrCodes = ShaderError::Codes; +const std::string ShaderError::type = "nb::ShaderError"; +const ErrorCodeMap ShaderError::ErrorMessages = { + {ShaderErrCodes::UNDEFINED, "Error"} +}; + +using ProgramErrCodes = ProgramError::Codes; +const std::string ProgramError::type = "nb::ProgramError"; +const ErrorCodeMap ProgramError::ErrorMessages = { + {ProgramErrCodes::UNDEFINED, "Error"}, + {ProgramErrCodes::LINKING_ERROR, "Linker error"} +}; + +Shader::Shader(GLenum target_, const std::string& source_) : target(target_) { + declare(); + const char* src_ptr= source_.data(); + glShaderSource(_id, 1, &src_ptr, NULL); + glCompileShader(_id); + _success = status(GL_COMPILE_STATUS); + if (!_success) { + WARN(log(), 0x0FE); + } +} + +Shader::Shader(Shader&& cpy) : target(cpy.target) { + _id = cpy._id; +} + +Shader::operator bool() { return _success; } + +GLint Shader::status(GLenum parameter) const { + GLint param = 0; + glGetShaderiv(_id, parameter, ¶m); + return param; +} + +std::string Shader::log() const { + GLint logsize = status(GL_INFO_LOG_LENGTH); + char* log_ = new char[logsize]; + glGetShaderInfoLog(_id, logsize, NULL, log_); + std::string ret(log_, logsize); + delete[] log_; + return ret; +} + +Program::Program(SharedVector shaders_) { + declare(); + for (auto shad_ptr : shaders_) { + glAttachShader(_id, shad_ptr->id()); + } + glLinkProgram(_id); + _success = status(GL_LINK_STATUS); + if (!_success) { + WARN(log(), 0x0FE); + } +} + +Program::operator bool() { return _success; } + +GLint Program::status(GLenum parameter) const { + GLint param = 0; + glGetProgramiv(_id, parameter, ¶m); + return param; +} + +std::string Program::log() const { + GLint logsize = status(GL_INFO_LOG_LENGTH); + char* log_ = new char[logsize]; + glGetProgramInfoLog(_id, logsize, NULL, log_); + std::string ret(log_, logsize); + delete[] log_; + return ret; +} + + + +} \ No newline at end of file diff --git a/engine/NBGraphics/src/Textures.cpp b/engine/NBGraphics/src/Textures.cpp new file mode 100644 index 0000000..9b448c7 --- /dev/null +++ b/engine/NBGraphics/src/Textures.cpp @@ -0,0 +1,10 @@ +#include "Textures.hpp" + +namespace nb{ + +Texture::Texture(GLenum target_) : target(target_) { + glGenTextures(1, &_id); +} + + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/VAOManager.cpp b/engine/NBGraphics/src/VAOManager.cpp deleted file mode 100644 index c9ea7d5..0000000 --- a/engine/NBGraphics/src/VAOManager.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "VAOManager.hpp" - -namespace NB { - -using BufferManagerPointer = std::shared_ptr; - -VAOManager::VAOManager() { glGenVertexArrays(1, &_id); } - -VAOManager::VAOManager( - std::vector vert_bufs, - BufferManagerPointer elmt_buf, - const VertexAttributeList& vert_attrs -) : VAOManager() { - _elmt_buffer = elmt_buf; - for (BufferManagerPointer vb : vert_bufs) { - _vert_buffers[vb->id()] = vb; - } - if (vert_attrs.size()!=0) { - generate(vert_attrs); - } -} - -VAOManager::VAOManager(VAOManager&& rhs) { *this = std::move(rhs); } - -VAOManager& VAOManager::operator=(VAOManager&& rhs) { - remove(); - _vert_buffers = rhs._vert_buffers; - _elmt_buffer = rhs._elmt_buffer; - _vert_attrs = rhs._vert_attrs; - return *this; -} - -VertexAttributeList VAOManager::getLayout() const { return _vert_attrs; } - -GLuint VAOManager::id() const { return _id; } - -bool VAOManager::isInitialized() const { - return _id && glIsVertexArray(_id); -} - -void VAOManager::remove() const { - bind(); - for (int i{0}; i < _vert_attrs.size(); ++i) { - glDisableVertexAttribArray(i); - } - unbind(); - glDisableVertexAttribArray(_id); -} - -unsigned int VAOManager::vertSize(GLuint id) const { - unsigned int size = 0; - for (const VertexAttribute& va : _vert_attrs) { - if (va.ptr.buffer == id) { - size += va.GLSLSize * GLSLTypeSize(va.GLSLType); - } - } - return size; -} - -std::vector VAOManager::getVertexBuffers() const { - std::vector ret; - for (const auto& i : _vert_buffers) { - ret.emplace_back(i.second); - } - return ret; -} - -RawVec VAOManager::attributeData(unsigned int i) { - if (i <= _vert_attrs.size()) { - throw std::out_of_range("No vertex attribute exists for specified index"); - } - VertexAttribute attr = _vert_attrs[i]; - BufferManagerPointer buffer = _vert_buffers[attr.ptr.buffer]; - RawVec data = buffer->data(); - unsigned int attr_size = attr.GLSLSize*GLSLTypeSize(attr.GLSLType); - RawVec ret((buffer->size()-attr.ptr.offset-attr_size)*attr_size/attr.ptr.stride); - int data_size = data.size(); - for (int i{0}; i < data_size; ++i) { - if (isIndexInVertexAttribute(i, attr)) { - ret[i] = data[i]; - } - } - return ret; -} - -RawVec VAOManager::attributeData(unsigned int i, const RawVec& new_data) { - if (i >= _vert_attrs.size()) { - throw std::out_of_range("No vertex attribute exists for specified index."); - } - VertexAttribute attr = _vert_attrs[i]; - BufferManagerPointer buffer = _vert_buffers[attr.ptr.buffer]; - unsigned int attr_size = attr.GLSLSize*GLSLTypeSize(attr.GLSLType); - unsigned int offset = attr.ptr.offset; - unsigned int num_verts = buffer->size() / vertSize(buffer->id()); - if (num_verts*attr_size != new_data.size()) { - // WHY HERE???? - THROW_VAO_ERROR( - "Input data size of " + std::to_string(new_data.size()) - + " does not match data size for requested vertex attribute of total size " + std::to_string(num_verts*attr_size) - + "." - ); - } - uint64_t pos = uint64_t(new_data.data()); - for (int i{0}; idata((void*)(pos+i*attr_size), offset+i*attr.ptr.stride, attr_size); - } - return RawVec(new_data); -} - -void VAOManager::bind() const { - glBindVertexArray(_id); - if (_elmt_buffer) { _elmt_buffer->bind(); } -} - -void VAOManager::unbind() const { - glBindVertexArray(0); - if (_elmt_buffer) { _elmt_buffer->unbind(); } -} - -void VAOManager::addVBO(BufferManagerPointer vert_buf) { - GLuint vert_id = vert_buf->id(); - if (_vert_buffers.find(vert_id) == _vert_buffers.end()) { - _vert_buffers[vert_buf->id()] = vert_buf; - } else { - THROW_VAO_ERROR("Attempting to add identical VBO id of " + std::to_string(vert_id) + "."); - } -} - -void VAOManager::changeEBO(BufferManagerPointer ebo) { - _elmt_buffer = ebo; -} - -VertexAttributeList VAOManager::addVertexAttributes(const VertexAttributeList& vert_attrs) { - VertexAttributeList curr_vas = _vert_attrs; - curr_vas.insert(curr_vas.end(), vert_attrs.begin(), vert_attrs.end()); - try { - throw checkValid(curr_vas); - } catch (VAOError vaoe) { - if (vaoe.error) { - THROW_VAO_ERROR(vaoe.what()); - } - } - _vert_attrs = curr_vas; - generate(); - return _vert_attrs; -} - -VertexAttributeList VAOManager::addVertexAttributes(VertexAttribute vert_attr) { - return addVertexAttributes({vert_attr}); -} - -VertexAttributeList VAOManager::generate() { - bind(); - unsigned int num_attrs = _vert_attrs.size(); - VertexAttribute* va; - for (int i = 0; i < num_attrs; ++i) { - va = &(_vert_attrs[i]); - _vert_buffers[va->ptr.buffer]->bind(); - glVertexAttribPointer( - i, - va->GLSLSize, - va->GLSLType, - va->GLSLNormalization, - va->ptr.stride, - (void*)va->ptr.offset - ); - glVertexAttribDivisor(i, va->ptr.divisor); - glEnableVertexAttribArray(i); - } - if (_elmt_buffer != nullptr) { - _elmt_buffer->bind(); - } - unbind(); - return _vert_attrs; -} - -VertexAttributeList VAOManager::generate(VertexAttributeList vert_attrs) { - try { - throw checkValid(vert_attrs); - } catch (VAOError vaoe) { - if (vaoe.error) { - THROW_VAO_ERROR(vaoe.what()); - } - } - _vert_attrs.swap(vert_attrs); - return generate(); -} - -VertexAttributeList VAOManager::changeLayout(unsigned int i, VertexAttributePointer vap) { - _vert_attrs[i].ptr = vap; - bind(); - const VertexAttribute* va = &(_vert_attrs[i]); - glDisableVertexAttribArray(i); - _vert_buffers[va->ptr.buffer]->bind(); - glVertexAttribPointer( - _id, - va->GLSLSize, - va->GLSLType, - va->GLSLNormalization, - va->ptr.stride, - (void*)va->ptr.offset - ); - glVertexAttribDivisor(i, va->ptr.divisor); - glEnableVertexAttribArray(i); - unbind(); - return _vert_attrs; -} - -VAOError VAOManager::checkValid(const VertexAttributeList& vert_attrs) { - GLuint va_id; - unsigned int num_attrs = vert_attrs.size(); - for (int i = 0; i < num_attrs; ++i) { - va_id = vert_attrs[i].ptr.buffer; - if (_vert_buffers.find(va_id) == _vert_buffers.cend()) { - return VAOError("Attempting to point to unknown VBO of id " + std::to_string(va_id) + - " at Vertex Attribute " + std::to_string(i) + "."); - } - if (vert_attrs[i].ptr.stride<0) { - return VAOError("Invalid stride value at Vertex Attribute " + std::to_string(i) + "."); - } - } - return VAOError(false); -} - -} // namespace NB diff --git a/engine/NBGraphics/src/VertexArray.cpp b/engine/NBGraphics/src/VertexArray.cpp new file mode 100644 index 0000000..f7ae2d3 --- /dev/null +++ b/engine/NBGraphics/src/VertexArray.cpp @@ -0,0 +1,231 @@ +#include "VertexArray.hpp" + +namespace nb { + +using VAOErrorCodes = VAOError::Codes; +const std::string VAOError::type = "nb::VAOError"; +const ErrorCodeMap VAOError::ErrorMessages = { + { VAOErrorCodes::UNDEFINED, "Error" }, + { VAOErrorCodes::INVALID_ATTRIBUTE, "Targeting invalid attribute"}, + { VAOErrorCodes::INVALID_VAO, "Targeting invalid VAO" } +}; + +VAO::VAO(const VertexAttributePointerList& attr_ptrs) { + attributes(attr_ptrs); +} + +VAO::VAO(VAO&& other) { + *this = std::move(other); +} + +VAO& VAO::operator=(VAO&& rhs) { + _id = rhs._id; + _attrs = rhs._attrs; + + rhs._id = 0; + rhs._attrs = {}; + + return *this; +} + +VertexAttributePointerList VAO::attributes() const { + return _attrs; +} + +VertexAttributePointerList VAO::attributes(const VertexAttributePointerList& attrs_) { + declare(); + disable(); + _attrs = attrs_; + bind(); + for (auto attr_ptr : attrs_) { + glBindBuffer(GL_ARRAY_BUFFER, attr_ptr.buffer); + GLuint idx = attr_ptr.index; + glVertexAttribPointer( + idx, + attr_ptr.attribute.GLSLSize, + attr_ptr.attribute.GLSLType, + attr_ptr.attribute.GLSLNormalization, + attr_ptr.attribute.layout.stride, + (void*)attr_ptr.attribute.layout.offset + ); + glEnableVertexAttribArray(idx); + } + unbind(); + return _attrs; +} + +VertexAttributePointer VAO::attr(GLuint idx) const { + for (auto attr_ptr : _attrs) { + if (idx == attr_ptr.index) { + return attr_ptr; + } + } + THROW(VAOError(Codes::INVALID_ATTRIBUTE)); +} + +void VAO::enable() const { + if (_id) { + bind(); + for(auto attr_ptr : _attrs) { + glEnableVertexAttribArray(attr_ptr.index); + } + unbind(); + } +} + +void VAO::enable(GLuint idx) const { + if(_id) { + bind(); + glEnableVertexAttribArray(attr(idx).index); + unbind(); + } +} + +void VAO::disable() const { + if(_id) { + bind(); + for(auto attr_ptr : _attrs) { + glDisableVertexAttribArray(attr_ptr.index); + } + unbind(); + } +} + +void VAO::disable(GLuint idx) const { + if(_id) { + bind(); + glDisableVertexAttribArray(attr(idx).index); + unbind(); + } +} + +VertexGroup::VertexGroup(VertexGroup&& other) { + *this = std::move(other); +} + +VertexGroup& VertexGroup::operator=(VertexGroup&& rhs) { + _vertex_data = rhs._vertex_data; + _ebo = rhs._ebo; + _vao = rhs._vao; + + rhs._vertex_data = {}; + rhs._ebo = nullptr; + rhs._vao = nullptr; + + return *this; +} + +VertexGroup::VertexGroup(const VertexDataVec& vertex_data_) +: _vao(std::make_shared()), _ebo(std::make_shared()) { + setBuffers(vertex_data_); +} + +VertexGroup::VertexGroup(const VertexData& vertex_data_) +: VertexGroup(VertexDataVec({vertex_data_})) {} + +VertexGroup::VertexGroup( + const ByteVector& data_, + const VertexAttributeList& attrs_ +) : VertexGroup({std::make_shared(data_), attrs_}) {} + +size_t VertexGroup::addBuffer(const VertexData& vertex_data_) { + VertexData data = vertex_data_; + _vertex_data.emplace_back(data); + VertexAttributePointerList attrs = _vao->attributes(); + GLuint idx = attrs.size(); + for (auto attr : data.attrs) { + attrs.emplace_back( + VertexAttributePointer{attr, data.vbo->id(),idx} + ); + idx++; + } + _vao->attributes(attrs); + return _vertex_data.size(); +} + +VertexData VertexGroup::getBuffers(size_t idx) { + return _vertex_data[idx]; +} + + VertexDataVec VertexGroup::getBuffers() { + return _vertex_data; +} + +size_t VertexGroup::setBuffers(const VertexDataVec& vertex_data_) { + _vertex_data = vertex_data_; + VertexAttributePointerList attr_ptrs; + GLuint idx = 0; + for (auto data : _vertex_data) { + for (auto attr : data.attrs) { + attr_ptrs.emplace_back( + VertexAttributePointer{attr, data.vbo->id(), idx} + ); + idx++; + } + } + _vao->attributes(attr_ptrs); + return _vertex_data.size(); +} + +VertexData VertexGroup::dropBuffer(size_t idx) { + if (idx >= _vertex_data.size()) { + THROW(BufferError(BufferError::Codes::INVALID_BUFFER)); + } + VertexDataVec tmp; + VertexData ret; + size_t i = 0; + for (auto buf : _vertex_data) { + if (i == idx) { + ret = tmp[idx]; + } else { + tmp.emplace_back(buf); + } + i++; + } + setBuffers(tmp); + return ret; +} + +VertexAttributePointer VertexGroup::attribute(size_t idx_) const { + return _vao->attr(idx_); +} + +void VertexGroup::enableAttr(GLuint idx) { + bind(); + _vao->enable(idx); + unbind(); +} + +void VertexGroup::disableAttr(GLuint idx) { + bind(); + _vao->disable(idx); + unbind(); +} + +void VertexGroup::disableBuffer(size_t idx) { + auto buf_idx = _vertex_data[idx].vbo->id(); + bind(); + for (auto attr : _vao->attributes()) { + if (attr.buffer == buf_idx) { + _vao->disable(attr.index); + } + } + unbind(); +} + +void VertexGroup::enableBuffer(size_t idx) { + auto buf_idx = _vertex_data[idx].vbo->id(); + bind(); + for (auto attr : _vao->attributes()) { + if (attr.buffer == buf_idx) { + _vao->enable(attr.index); + } + } + unbind(); +} + +void VertexGroup::enable() { _vao->enable(); } + +void VertexGroup::disable() { _vao->disable(); } + +} // namespace nb diff --git a/engine/NBGraphics/src/Window.cpp b/engine/NBGraphics/src/Window.cpp index adceaf7..7cffeca 100644 --- a/engine/NBGraphics/src/Window.cpp +++ b/engine/NBGraphics/src/Window.cpp @@ -16,9 +16,23 @@ static std::map defailt_window_hints = { #endif }; -GLError::GLError(const std::string& msg) : std::runtime_error(msg) {} +using OpenGLErrorCodes = OpenGLError::Codes; +const std::string OpenGLError::type = "nb::OpenGLError"; +const ErrorCodeMap OpenGLError::ErrorMessages = { + {OpenGLErrorCodes::UNDEFINED, "Error"}, + {OpenGLErrorCodes::INIT_FAILED, "GLFW initialization failed"}, + {OpenGLErrorCodes::GLFW_INTIALIZED, "GLFW has already been initialized"}, + {OpenGLErrorCodes::GLAD_FAILED, "GLAD initialization failed"} +}; -WindowError::WindowError(const std::string &msg) : std::runtime_error(msg) {} +using WindowErrorCodes = WindowError::Codes; +const std::string WindowError::type = "nb::WindowError"; +const ErrorCodeMap WindowError::ErrorMessages = { + {WindowErrorCodes::UNDEFINED, "Error"}, + {WindowErrorCodes::INITIALIZED_WINDOW, "Window already initialized"}, + {WindowErrorCodes::NO_GLFW, "GLFW has not been initialized"}, + {WindowErrorCodes::INIT_FAILED, "Could not intialized window"} +}; int Window::getGLFWHint(int hint_key) { if (GLFWHints.find(hint_key) == GLFWHints.end()) { @@ -30,7 +44,7 @@ int Window::getGLFWHint(int hint_key) { int Window::setGLFWHint(int hint_key, int hint_val) { if (Window::_glfw_init) { - throw GLError("Cannot set GLFW hint after window is initialized."); + THROW(OpenGLError(OpenGLErrorCodes::GLFW_INTIALIZED)); } else { GLFWHints[hint_key] = hint_val; } @@ -60,7 +74,7 @@ Window::Window(const uint16_t x, const uint16_t y, const char* initName, GLFWmon Window::_glfw_init = true; } else { if (Window::StrictInitialization) { - throw GLError("Failed to load GLFW with glfwInit()="+std::to_string(glfwResponse)+"."); + THROW(OpenGLError(OpenGLErrorCodes::INIT_FAILED)); } } } @@ -103,7 +117,7 @@ int Window::getWindowHint(int hint_key) const { int Window::setWindowHint(int hint_key, int hint_val) { if (_init) { - throw WindowError("Cannot set window hint after window is initialized."); + THROW(WindowError(WindowErrorCodes::INITIALIZED_WINDOW)); } else { windowHints[hint_key] = hint_val; } @@ -112,7 +126,7 @@ int Window::setWindowHint(int hint_key, int hint_val) { int Window::init() { if (!_glfw_init) { - throw GLError("GLFW has not been initialized."); + THROW(WindowError(WindowErrorCodes::NO_GLFW)); } for (const auto& hint : windowHints) { glfwWindowHint(hint.first, hint.second); @@ -123,7 +137,7 @@ int Window::init() { Window::WindowContexts.erase(window); if (Window::WindowContexts.size()==0 && Window::_glfw_init) { glfwTerminate(); } if (Window::StrictInitialization) { - throw WindowError("Could not create window in glfwCreateWindow()."); + THROW(WindowError(WindowErrorCodes::INIT_FAILED)); } } glfwMakeContextCurrent(window); @@ -133,10 +147,7 @@ int Window::init() { if (!gladResponse) { Window::checkKillGLFW(); if (Window::StrictInitialization) { - throw GLError("Failed to load GLAD with \ - gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)="+\ - std::to_string(gladResponse)+"." - ); + THROW(OpenGLError(OpenGLErrorCodes::GLAD_FAILED)); } } diff --git a/engine/NBGraphics/src/shader.cpp b/engine/NBGraphics/src/shader.cpp deleted file mode 100644 index f24d86e..0000000 --- a/engine/NBGraphics/src/shader.cpp +++ /dev/null @@ -1,742 +0,0 @@ -#include "Shader.h" - -// #define _PREPROC_FUNC_PARAM_NAMES_ (const ShaderPreprocessor& proc, ShaderUnit& shad, unsigned int linenum, const std::string& line, std::vector& params) - -namespace NB{ - -File::FilePath get_file_path(std::string name) { - const std::vector allowed_special_chars = { - '.', '!', '#', '$', '%', '&', '\'', '(', ')', '+', ',', '-', ';', '=', '@', '[', ']', '^', '_', '`', '~' - }; - std::vector path = {""}; - for (const char c : name) { - if (c == '/' || c == '\\') { - path.push_back(""); - } else if (std::isalnum(c)) { - path.back() += c; - } else { - bool spec_char_found = false; - for (const char sc : allowed_special_chars) { - if (c == sc) { - path.back() += c; - spec_char_found = true; - break; - } - } - if (!spec_char_found) { - throw std::runtime_error("'" + name + "' is not valid filepath."); - } - } - } - File::FilePath ret; - for (const auto& tk : path) { - if (tk == path.back()) { - size_t period = tk.find("."); - if (period == std::string::npos) { - ret.basename = tk; - ret.ext = ""; - } else { - ret.basename = tk.substr(0, period); - ret.ext = tk.substr(period); - } - } else { - ret.dir += tk + "/"; - } - } - return ret; -} - -// ShaderPreprocessorError class -ShaderPreprocessorError::ShaderPreprocessorError( - const std::string& msg, - const std::string& file, - int line -) : code(Codes::UNDEFINED), std::runtime_error(formatDebugString(msg, file, line)) {} - -ShaderPreprocessorError::ShaderPreprocessorError( - Codes err_code, - const std::string& arg, - const std::string& file, - int line -) : code(err_code), std::runtime_error(formatDebugString(errorCodeParser(err_code, arg), file, line)) {} - -std::string ShaderPreprocessorError::errorCodeParser(Codes err_code, const std::string& arg) { - switch(err_code) { - case Codes::FILE_NOT_FOUND: - return "File '" + arg + "' not found."; - case Codes::BUILTIN_NOT_FOUND: - return "Built-in '" + arg + "' not found."; - case Codes::CUSTOM: - case Codes::UNDEFINED: - return arg; - case Codes::NONE: - default: - return ""; - } -} - -// ShaderError class -ShaderError::ShaderError( - const std::string& msg, - const char* shad, - const std::string& file, - int line -) : std::runtime_error(formatString(msg, shad, file, line)) {} - -std::string ShaderError::formatString( - const std::string& msg, - const char* shad, - const std::string& file, - int line -) { - std::stringstream ret; - if (file != "") { - ret << "In file " << file; - if (line >= 0) { - ret << " at line " << line; - } - ret << ":\n\t"; - } - ret << msg; - if (shad != nullptr) { - ret << " with shader error: " << shad; - } - return ret.str(); -} - -// ShaderPreprocessor class -std::string ShaderPreprocessor::TokenName(const ShaderPreprocessor::TokenType& x) { - switch(x) { - case TK: - return "Token"; - break; - case DR: - return "Directive"; - break; - case WS: - return "Whitespace"; - break; - case NL: - return "NewLine"; - break; - case LC: - return "LineComment"; - break; - case BC: - return "BlockComment"; - break; - default: - return "Unrecognized"; - break; - } -} - -ShaderPreprocessor::ShaderPreprocessor() {} - -File ShaderPreprocessor::loadFromBase(const std::string path, const std::string base) const { - File ret; - std::ifstream filestream; - filestream.open(base + path); - if (filestream.is_open()) { - ret.path = get_file_path(base + path); - ret.src << filestream.rdbuf(); - return ret; - } - filestream.close(); - std::string ext; - for (const auto& kv : AcceptedExtensions) { - ext = kv.first; - filestream.open(base + path + ext); - if (filestream.is_open()) { - ret.path = get_file_path(base + path + ext); - ret.src << filestream.rdbuf(); - return ret; - } - filestream.close(); - } - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, base + path); -} - -File ShaderPreprocessor::loadFromDirectories(const std::string name) const { - File ret; - std::ifstream fstream; - for (const std::string& path : Directories) { - fstream.open(path + name); - if (fstream.is_open()) { - ret.path = get_file_path(path + name); - ret.src << fstream.rdbuf(); - return ret; - } - fstream.close(); - } - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, name); -} - -File ShaderPreprocessor::load(const std::string path, bool builtin_first) const { - if(builtin_first) { - return load_BuiltInFirst(path); - } - return load_FilesFirst(path); -} - -File ShaderPreprocessor::loadFromBuiltIn(const std::string name) const { - File ret; - //std::stringstream ret; - decltype(BuiltIns)::const_iterator builtin_it = BuiltIns.find(name); - if (builtin_it != BuiltIns.end()) { - ret.path = File::FilePath{"builtin:", name, ""}; - ret.src << builtin_it->second; - return ret; - } - throw ShaderPreprocessorError(Codes::BUILTIN_NOT_FOUND, name); -} - -File ShaderPreprocessor::load_FilesFirst(const std::string path) const { - try { - return loadFromBase(path); - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - try { - return loadFromDirectories(path); - } catch (ShaderPreprocessorError f) { - if (f.code == Codes::FILE_NOT_FOUND) { - try { - return loadFromBuiltIn(path); - } catch(ShaderPreprocessorError g) { - if (g.code == Codes::BUILTIN_NOT_FOUND) { - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, path); - } else { throw g; } - } - } else { throw f; } - } - } else { throw e; } - } -} - -File ShaderPreprocessor::load_BuiltInFirst(const std::string path) const { - try { - return loadFromBuiltIn(path); - } catch (ShaderPreprocessorError f) { - if (f.code == Codes::BUILTIN_NOT_FOUND) { - try { - return loadFromDirectories(path); - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - return loadFromBase(path); - } else { - throw e; - } - } - } else { - throw f; - } - } - -} - -std::vector ShaderPreprocessor::tokenize(const std::string& code) const { - enum State { - FSlash, - WhiteSpace, - LineComment, - BlockComment, - BlockCommentEndStar, - Directive, - Token - }; - - std::vector tks; - - std::string token = ""; - State state = WhiteSpace; - for(char c : code) { - if (c==13) { - continue; - } - switch(state) { - case WhiteSpace: - if (c=='/') { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = FSlash; - } else if (c=='#') { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = Directive; - } else if (c=='\n') { - if (token != "") { tks.push_back({WS, token}); } - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - token += c; - } else { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = Token; - } - break; - case FSlash: - if (c=='/') { - token += c; - state = LineComment; - } else if (c=='*') { - token += c; - state = BlockComment; - } else if (c=='\n') { - tks.push_back({TK, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - tks.push_back({TK, token}); - token = c; - }else { - token += c; - state = Token; - } - break; - case LineComment: - if (c=='\n') { - tks.push_back({LC, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else { - token += c; - } - break; - case BlockComment: - token += c; - if (c=='*') { - state = BlockCommentEndStar; - } - break; - case BlockCommentEndStar: - token += c; - if (c=='/') { - tks.push_back({BC, token}); - token = ""; - state = WhiteSpace; - } else { - state = BlockComment; - } - break; - case Directive: - if (c=='\n') { - tks.push_back({DR, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (c=='/') { - tks.push_back({DR, token}); - token = c; - state = FSlash; - } else { - token += c; - } - break; - case Token: - if (c=='\n') { - tks.push_back({TK, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - tks.push_back({TK, token}); - token = c; - state = WhiteSpace; - } else if (c=='/') { - tks.push_back({TK, token}); - token = c; - state = FSlash; - }else { - token += c; - } - break; - default: - break; - } - - } - - switch(state) { - case WhiteSpace: - if (token != "") { tks.push_back({WS, token}); } - case FSlash: - tks.push_back({TK, token}); - break; - case LineComment: - tks.push_back({LC, token}); - break; - case BlockComment: - case BlockCommentEndStar: - tks.push_back({BC, token}); - break; - case Directive: - tks.push_back({DR, token}); - break; - case Token: - tks.push_back({TK, token}); - break; - default: - break; - } - - return tks; -} - -ShaderUnit& ShaderPreprocessor::preprocess(const std::string& code, ShaderUnit& shad) const { - typedef ShaderPreprocessor::Token Token; - std::vector tks = tokenize(code); - - for (int i{0}; i < tks.size(); ++i) { - switch(tks[i].first) { - case DR: - directive_dispatch(shad, tks[i].second); - break; - case TK: - case NL: - case LC: - case BC: - case WS: - default: - shad.preprocSource += tks[i].second; - break; - } - } - - if (shad.vMajor == 0 && shad.vMinor == 0) { - shad.vMajor = 1; - shad.vMinor = 10; - } - return shad; -} - -ShaderUnit ShaderPreprocessor::preprocess(File file, GLenum shader_type) const { - ShaderUnit ret; - ret.file = file; - if (shader_type) { - ret.type = shader_type; - } else { - decltype(AcceptedExtensions.begin()) find_type = AcceptedExtensions.find(file.path.ext); - if (find_type != AcceptedExtensions.end()) { - ret.type = find_type->second; - } - } - preprocess(file.src.str(), ret); - return ret; -} - -ShaderUnit ShaderPreprocessor::preprocess(const std::string& code, GLenum shader_type) const { - File local; - local.path = File::FilePath{"live:", "live", ".shad"}; - return preprocess(local, shader_type); -} - -ShaderProgram ShaderPreprocessor::CreateShaderProgram(std::vector shads) const { - std::vector _shader_units; - for (const auto& name : shads) { - _shader_units.push_back(preprocess(load(name))); - } - return ShaderProgram::CreateShaderProgram(_shader_units); -} - -ShaderProgram ShaderPreprocessor::ReloadFromFile(const ShaderProgram& rhs) const { - std::vector shader_names; - shader_names.reserve(rhs._shader_units.size()); - std::string filename; - for (const ShaderUnit& shad : rhs._shader_units) { - filename = shad.file.path.dir + shad.file.path.basename + shad.file.path.ext; - shader_names.emplace_back(filename); - } - return CreateShaderProgram(shader_names); -} - -bool ShaderPreprocessor::directive_dispatch(ShaderUnit& shad, const std::string& line) const { - StringVec dir_tks = {""}; - for (char c : line) { - if (std::isblank(c)) { - if (dir_tks.back() != "") { - dir_tks.push_back(""); - } - } else { - dir_tks.back() += c; - } - } - if (!dir_tks.size()) { return false; } - - if (dir_tks[0][0] != '#') { return false; } - - if (dir_tks[0] == "#define") { - return preprocessor_define(shad, dir_tks, line); - } else if (dir_tks[0] == "#version") { - return preprocessor_version(shad, dir_tks, line); - } else if (dir_tks[0] == "#include") { - return preprocessor_include(shad, dir_tks, line); - } - return false; -} - -typedef std::vector StringVec; - -bool ShaderPreprocessor::preprocessor_include( - ShaderUnit& shad, - const StringVec& tokens, - const std::string& line -) const -{ - try { - if (tokens[0] != "#include") { return false; } - } catch (std::out_of_range e) { - return false; - } - std::string path = ""; - for (const auto& tk : tokens) { - if (tk != tokens.front()) { - path += tk; - } - } - // Add file-inclusion base + - // Do path cleanup + - // Restructure preprocessing data flow - std::string filename = path.substr(1, path.size()-2); - try { - if (path[0] == '"' && path.back() == '"') { - preprocess(load(filename).src.str(), shad); - return true; - } else if (path[0] == '<' && path.back() == '>') { - preprocess(load(filename, true).src.str(), shad); - return true; - } - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - std::cout << "COULD NOT FIND " << filename << ".\n"; - } else { - throw e; - } - } - return false; -} - -bool ShaderPreprocessor::preprocessor_define(ShaderUnit& shad, const StringVec& tokens, const std::string& line) const { - shad.preprocSource += line; - try { - if (tokens[0] != "#define") { return false; } - try { - shad.defines[tokens[1]] = tokens[2]; - } catch (std::length_error& f) { - shad.defines[tokens[1]] = ""; - } - return true; - } catch (std::out_of_range& e) { - return false; - } -} - -bool ShaderPreprocessor::preprocessor_version(ShaderUnit& shad, const StringVec& tokens, const std::string& line) const { - shad.preprocSource += line; - std::cout << "Shader version: "; - try { - if (tokens[0] != "#version") { return false; } - if (tokens[1].size() == 3) { - short vMajor = tokens[1][0]-'0'; - short vMinorA = tokens[1][1]-'0'; - short vMinorB = tokens[1][2]-'0'; - if (vMajor > 10 || vMinorA > 10 || vMinorB > 10) { - return false; - } - shad.vMajor = vMajor; - shad.vMinor = vMinorA*10 + vMinorB; - shad.profile = AcceptedProfiles.at(tokens[2]); - std::cout << shad.vMajor << "." << shad.vMinor << "\n"; - return true; - } else { - return false; - } - } catch (std::out_of_range& e) { - return false; - } -} - - -/* bool ShaderPreprocessor::preprocessor_uniform( - ShaderUnit& shad, - const std::string& type, - const std::string& name -) const { - std::string type_str = ""; - unsigned int type_str_len = 0; - for (const char& c : type) { - if (type_str_len) { - if (std::isdigit(c)) { - type_str += c; - continue; - } else if (c=='[') { - type_str += '_'; - }else if (c == ']' || std::isblank(c)){ - continue; - } else { - return false; - } - } else { - if (std::isalnum(c)) { - type_str += c; - continue; - } else if (c == '[') { - type_str_len = type_str.length(); - type_str += '_'; - continue; - } else { - return false; - } - } - } - std::string name_str = ""; - unsigned int name_str_len = 0; - for (const char& c : name) { - if (c == ';') { - break; - } - if (name_str_len) { - if (std::isdigit(c)) { - type_str.insert(type_str_len, 1, c); - type_str_len++; - continue; - } else if (c=='[') { - type_str.insert(type_str_len, "_"); - type_str_len++; - continue; - }else if (c == ']' || std::isblank(c)){ - continue; - } else { - return false; - } - } else { - if (std::isalnum(c)) { - name_str += c; - continue; - } else if ( c == '[') { - name_str_len = name_str.length(); - type_str.insert(type_str_len, "_"); - type_str_len++; - continue; - } else { - return false; - } - } - } - shad.uniforms.push_back(UniformHandle{ - name_str, - type_str, - 0x0, - 0x0 - }); - return true; -} */ - - -// ShaderProgram - -ShaderProgram::ShaderProgram(ShaderProgram&& rhs) { - _shader_units = rhs._shader_units; - _id = rhs._id; - rhs._id = 0; -} - -ShaderProgram::~ShaderProgram() { - glDeleteProgram(_id); -} - -ShaderProgram& ShaderProgram::operator=(ShaderProgram&& rhs) { - _shader_units = rhs._shader_units; - _id = rhs._id; - rhs._id = 0; - return *this; -} - - -ShaderProgram ShaderProgram::CreateShaderProgram(std::vector& shaders) { - int success; - ShaderProgram ret(shaders); - char infoLog[512]; - unsigned int shad_id; - std::vector shad_ids; - shad_ids.reserve(shaders.size()); - for (auto& shad : ret._shader_units) { - const char* source = shad.preprocSource.data(); - shad_id = glCreateShader(shad.type); - shad_ids.emplace_back(shad_id); - glShaderSource(shad_id, 1, &source, NULL); - glCompileShader(shad_id); - glGetShaderiv(shad_id, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(shad_id, 512, NULL, infoLog); - File::FilePath& fp = shad.file.path; - std::string filename = fp.dir + fp.basename + fp.ext; - // std::cout << "Could not compile '" + filename + "': " << infoLog << "\n"; - // return *ret; - throw ShaderError("Could not compile '" + filename + "'.", infoLog); - } - } - - ret._id = glCreateProgram(); - for(auto& id : shad_ids) { - glAttachShader(ret._id, id); - } - glLinkProgram(ret._id); - glGetProgramiv(ret._id, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(ret._id, 512, NULL, infoLog); - throw ShaderError("Could not link shader program.", infoLog); - } - for (auto& id : shad_ids) { - glDeleteShader(id); - } - return ret; -} - -unsigned int ShaderProgram::id() const { return _id; } - -void ShaderProgram::use() const { - glUseProgram(_id); -} - -std::vector ShaderProgram::getShaders() const { - return _shader_units; -} - -ShaderUnit ShaderProgram::getShaders(unsigned int idx) const { - return _shader_units[idx]; -} - -void ShaderProgram::setBool(const std::string& name, bool value) const { - glUniform1i(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setInt(const std::string& name, int value) const { - glUniform1i(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setFloat(const std::string& name, float value) const { - glUniform1f(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setMat4(const std::string& name, glm::mat4& value) const { - glUniformMatrix4fv(glGetUniformLocation(_id, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); -} - -/* // Shader class -Shader::Shader() { _id = 0x0; } - -Shader::Shader(const Shader& cpy_shader) { _id = cpy_shader._id; } - -Shader& Shader::operator=(const Shader& cpy_shader) { _id = cpy_shader._id; return *this; } - -void Shader::use() const{ - glUseProgram(_id); -} - -*/ - -} \ No newline at end of file diff --git a/engine/NBGraphics/tests/CMakeLists.txt b/engine/NBGraphics/tests/CMakeLists.txt new file mode 100644 index 0000000..8e6d9d6 --- /dev/null +++ b/engine/NBGraphics/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.26.0) + +if (NB_BUILD_TESTS) + enable_testing() + include(GoogleTest) + add_executable(TestWindow + ./TestWindow.cpp + ) + target_link_libraries(TestWindow + NBCore + NBGraphics + ) + +endif() \ No newline at end of file diff --git a/engine/NBGraphics/tests/TestWindow.cpp b/engine/NBGraphics/tests/TestWindow.cpp new file mode 100644 index 0000000..daacb70 --- /dev/null +++ b/engine/NBGraphics/tests/TestWindow.cpp @@ -0,0 +1,66 @@ +#include "GLLoad.hpp" +#include "ProgramPipeline.hpp" +#include "VertexArray.hpp" +#include "Window.hpp" + +int main() { + nb::logger.log("Howdy!"); + nb::Window window(400, 400, "Hello!"); + window.setWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + window.setWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + window.init(); + + nb::logger.log("Goob"); + + auto vert = std::make_shared( + GL_VERTEX_SHADER, + "#version 330 core\n" + "layout (location = 0) in vec2 aPos;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);\n" + "}\0" + ); + + LOG("Vertex Shader: " + vert->log()); + + auto frag = std::make_shared( + GL_FRAGMENT_SHADER, + "#version 330 core\n" + "out vec4 FragColor;\n" + "void main()\n" + "{\n" + " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" + "}\n\0" + ); + + LOG("Fragment shader: " + frag->log()); + + nb::ByteVector data = nb::vectorToBytes({ + -0.5, -0.5, 0.5, -0.5, 0.0, 0.5 + }); + + nb::Program prog({vert, frag}); + prog.bind(); + + nb::VertexGroup tri(data, { + nb::VertexAttribute{ + 2, + GL_FLOAT, + false, + {0, 8} + } + }); + tri.bind(); + + GLFWwindow* window_ptr = window.getWindow(); + while(!glfwWindowShouldClose(window_ptr)) { + glDrawArrays(GL_TRIANGLES, 0, 3); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glfwPollEvents(); + glfwSwapBuffers(window_ptr); + } + + return 0; +} \ No newline at end of file