diff --git a/README.md b/README.md index a71a8c39581c817662d37fbafce41d91aaf457dc..22abbbeaf90e57eca25b4a08bc2c340a5dd3cdf5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # OSX - -clang++ -L./osx/lib/ -lSDL2 -I./osx/include/ main.cpp -framework OpenGL +clang++ -std=c++17 -L./osx/lib/ -lSDL2 -I./osx/include/ -I./include src/context.cpp src/app.cpp main.cpp -framework OpenGL # Windows diff --git a/include/app.hpp b/include/app.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4e56f3cacdf00618755ffd6d72d1d924cc3eb923 --- /dev/null +++ b/include/app.hpp @@ -0,0 +1,82 @@ +/** + * @file + * @author Benedict R. Gaster + */ +#pragma once + +#include <events.hpp> +#include <context.hpp> + +namespace uwe { + + class App { + private: + Context context_; + + bool output_framerate_; + public: + App(); + ~App(); + + void init(int width, int height); + void run(); + + /// toggles output framerate to console + void toggle_framerate() { + output_framerate_ = !output_framerate_; + } + + virtual void begin() = 0; + virtual void update() = 0; + virtual void draw() = 0; + + virtual void key_pressed(Scancode scancode, bool is_repeat) { + + }; + + virtual void key_released(Scancode scancode) { + + }; + + virtual void mouse_pressed() { + + }; + + virtual void mouse_released() { + + }; + + virtual void mouse_moved() { + + }; + + // API + int get_framebuffer_width() const { + return context_.get_tex_width(); + } + + int get_framebuffer_height() const { + return context_.get_tex_height(); + } + + uint8_t& framebuffer(size_t offset) { + return context_.pixel(offset); + } + + void set_framebuffer(size_t offset, Colour colour) { + context_.set_pixel(offset, colour); + } + + void clear(Colour colour) { + SDL_SetRenderDrawColor( + context_.get_renderer(), + colour.blue_, + colour.green_, + colour.red_, + colour.alpha_ ); + SDL_RenderClear( context_.get_renderer() ); + } + }; + + void run(); +} // namespace uwe \ No newline at end of file diff --git a/include/context.hpp b/include/context.hpp new file mode 100644 index 0000000000000000000000000000000000000000..68afe6e92f8e66ad4b692707ee84b4712c9ee5a4 --- /dev/null +++ b/include/context.hpp @@ -0,0 +1,122 @@ +/** + * @file + * @author Benedict R. Gaster + */ +#pragma once + +#include <vector> + +#include <SDL2/SDL.h> + +#include <events.hpp> + +namespace uwe { + class Colour { + public: + uint8_t blue_; + uint8_t green_; + uint8_t red_; + uint8_t alpha_; + + Colour(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha): + red_{red}, + green_{green}, + blue_{blue}, + alpha_{alpha} { + } + + static Colour red() { + return Colour{ 255, 0, 0, SDL_ALPHA_OPAQUE }; + } + + static Colour green() { + return Colour{ 0, 255, 0, SDL_ALPHA_OPAQUE }; + } + + static Colour blue() { + return Colour{ 0, 0, 255, SDL_ALPHA_OPAQUE }; + } + + static Colour white() { + return Colour{ 255, 255, 255, SDL_ALPHA_OPAQUE }; + } + + static Colour black() { + return Colour{ 0, 0, 0, SDL_ALPHA_OPAQUE }; + } + }; + + class Context { + public: + /** @brief Create a context + * @param width an integer setting the window width + * @param height an integer setting the window height + */ + Context(); + + /// Destory context + ~Context(); + + void init(int width, int height); + + /// Output details of GPU backend and texture formats + void dump_renderer_info(); + + /// run the control loop, returns only when user exits + //void run(App* app); + + int get_height() const { + return height_; + } + + int get_width() const { + return width_; + } + + SDL_Window* get_window() const { + return window_; + } + + SDL_Renderer* get_renderer() const { + return renderer_; + } + + SDL_Texture* get_texture() const { + return texture_; + } + + std::vector< uint8_t >& get_pixels() { + return pixels_; + } + + uint8_t& pixel(size_t offset) { + return pixels_[offset]; + } + + void set_pixel(size_t offset, Colour colour) { + pixels_[offset + 0] = colour.blue_; + pixels_[offset + 1] = colour.green_; + pixels_[offset + 2] = colour.red_; + pixels_[offset + 3] = colour.alpha_; + } + + int get_tex_width() const { + return tex_width_; + } + + int get_tex_height() const { + return tex_height_; + } + private: + int height_; ///< window height + int width_; ///< window width + int tex_width_; ///< texture width + int tex_height_; ///< texture height + SDL_Window* window_; ///< window handle + SDL_Renderer* renderer_; ///< graphics backend handle + SDL_Texture* texture_; ///< pixel buffer texture handle + + std::vector< uint8_t > pixels_; ///< pixels representing framebuffer + }; +} + diff --git a/include/events.hpp b/include/events.hpp new file mode 100644 index 0000000000000000000000000000000000000000..328beebfe51a4c0edee35a0b030c6e23d42fd77d --- /dev/null +++ b/include/events.hpp @@ -0,0 +1,71 @@ +/** + * @file + * @author Benedict R. Gaster + */ +#pragma once + +#include <vector> + +#include <SDL2/SDL.h> + +namespace uwe { + + // event types + enum class Event { + EVENT_QUIT = SDL_QUIT, + EVENT_KEYDOWN = SDL_KEYDOWN, + }; + + // scancodes + enum class Scancode { + D_0 = SDL_SCANCODE_0, + D_1 = SDL_SCANCODE_1, + D_2 = SDL_SCANCODE_2, + D_3 = SDL_SCANCODE_3, + D_4 = SDL_SCANCODE_4, + D_5 = SDL_SCANCODE_5, + D_6 = SDL_SCANCODE_6, + D_7 = SDL_SCANCODE_7, + D_8 = SDL_SCANCODE_8, + D_9 = SDL_SCANCODE_9, + + A = SDL_SCANCODE_A, + B = SDL_SCANCODE_B, + C = SDL_SCANCODE_C, + D = SDL_SCANCODE_D, + E = SDL_SCANCODE_E, + F = SDL_SCANCODE_F, + G = SDL_SCANCODE_G, + H = SDL_SCANCODE_H, + I = SDL_SCANCODE_I, + J = SDL_SCANCODE_J, + K = SDL_SCANCODE_K, + L = SDL_SCANCODE_L, + M = SDL_SCANCODE_M, + N = SDL_SCANCODE_N, + O = SDL_SCANCODE_O, + P = SDL_SCANCODE_P, + Q = SDL_SCANCODE_Q, + R = SDL_SCANCODE_R, + S = SDL_SCANCODE_S, + T = SDL_SCANCODE_T, + U = SDL_SCANCODE_U, + V = SDL_SCANCODE_V, + W = SDL_SCANCODE_W, + X = SDL_SCANCODE_X, + Y = SDL_SCANCODE_Y, + Z = SDL_SCANCODE_Z, + + LSHIFT = SDL_SCANCODE_LSHIFT, + RSHIFT = SDL_SCANCODE_RSHIFT, + RETURN = SDL_SCANCODE_RETURN, + BACKSPACE = SDL_SCANCODE_BACKSPACE, + SPACE = SDL_SCANCODE_SPACE, + + UP = SDL_SCANCODE_UP, + DOWN = SDL_SCANCODE_DOWN, + LEFT = SDL_SCANCODE_LEFT, + RIGHT = SDL_SCANCODE_RIGHT, + }; + +} // namespace uwe \ No newline at end of file diff --git a/include/graphics.hpp b/include/graphics.hpp index f630567cf862fd52ba492379b84d2a9024888868..9607504998c4634c88e95a3edeeefa718d25f4e3 100644 --- a/include/graphics.hpp +++ b/include/graphics.hpp @@ -8,7 +8,67 @@ #include <SDL2/SDL.h> +#include <drawcontext.hpp> + namespace uwe { + // event types + enum class Event { + EVENT_QUIT = SDL_QUIT, + EVENT_KEYDOWN = SDL_KEYDOWN, + }; + + // scancodes + enum class Scancode { + SCANCODE_0 = SDL_SCANCODE_0, + SCANCODE_1 = SDL_SCANCODE_1, + SCANCODE_2 = SDL_SCANCODE_2, + SCANCODE_3 = SDL_SCANCODE_3, + SCANCODE_4 = SDL_SCANCODE_4, + SCANCODE_5 = SDL_SCANCODE_5, + SCANCODE_6 = SDL_SCANCODE_6, + SCANCODE_7 = SDL_SCANCODE_7, + SCANCODE_8 = SDL_SCANCODE_8, + SCANCODE_9 = SDL_SCANCODE_9, + + SCANCODE_A = SDL_SCANCODE_A, + SCANCODE_B = SDL_SCANCODE_B, + SCANCODE_C = SDL_SCANCODE_C, + SCANCODE_D = SDL_SCANCODE_D, + SCANCODE_E = SDL_SCANCODE_E, + SCANCODE_F = SDL_SCANCODE_F, + SCANCODE_G = SDL_SCANCODE_G, + SCANCODE_H = SDL_SCANCODE_H, + SCANCODE_I = SDL_SCANCODE_I, + SCANCODE_J = SDL_SCANCODE_J, + SCANCODE_K = SDL_SCANCODE_K, + SCANCODE_L = SDL_SCANCODE_L, + SCANCODE_M = SDL_SCANCODE_M, + SCANCODE_N = SDL_SCANCODE_N, + SCANCODE_O = SDL_SCANCODE_O, + SCANCODE_P = SDL_SCANCODE_P, + SCANCODE_Q = SDL_SCANCODE_Q, + SCANCODE_R = SDL_SCANCODE_R, + SCANCODE_S = SDL_SCANCODE_S, + SCANCODE_T = SDL_SCANCODE_T, + SCANCODE_U = SDL_SCANCODE_U, + SCANCODE_V = SDL_SCANCODE_V, + SCANCODE_W = SDL_SCANCODE_W, + SCANCODE_X = SDL_SCANCODE_X, + SCANCODE_Y = SDL_SCANCODE_Y, + SCANCODE_Z = SDL_SCANCODE_Z, + + SCANCODE_LSHIFT = SDL_SCANCODE_LSHIFT, + SCANCODE_RSHIFT = SDL_SCANCODE_RSHIFT, + SCANCODE_RETURN = SDL_SCANCODE_RETURN, + SCANCODE_BACKSPACE = SDL_SCANCODE_BACKSPACE, + SCANCODE_SPACE = SDL_SCANCODE_SPACE, + + SCANCODE_UP = SDL_SCANCODE_UP, + SCANCODE_DOWN = SDL_SCANCODE_DOWN, + SCANCODE_LEFT = SDL_SCANCODE_LEFT, + SCANCODE_RIGHT = SDL_SCANCODE_RIGHT, + }; + class Context { public: /** @brief Create a context diff --git a/main.cpp b/main.cpp index b448c16fbaed9af6920a010be4fcb8e480257a33..738467ba722b71273d1bb52a178a7024030cd134 100644 --- a/main.cpp +++ b/main.cpp @@ -3,18 +3,76 @@ #include <vector> #include <cstring> -#include <graphics.hpp> +#include <context.hpp> +#include <app.hpp> const int width = 640; const int height = 480; -int main( int argc, char** argv ) -{ - uwe::Context context{width, height}; +class MyApp: public uwe::App { +private: + int width_; + int height_; +public: + MyApp(int width, int height); + ~MyApp(); - context.dump_renderer_info(); + void begin() override; + void update() override; + void draw() override; - context.run(); + void key_pressed(uwe::Scancode scancode, bool repeat) override; +}; + +MyApp::MyApp(int width, int height): + width_{width}, + height_{height} { + init(width, height); +} + +MyApp::~MyApp() { + +} + +void MyApp::begin() { + +} + +void MyApp::update() { + +} + +void MyApp::key_pressed(uwe::Scancode scancode, bool repeat) { + if( scancode == uwe::Scancode::L) { + toggle_framerate(); + } +} + +void MyApp::draw() { + clear(uwe::Colour::black()); + + for( unsigned int i = 0; i < 1000; i++ ) { + const unsigned int x = rand() % get_framebuffer_width(); + const unsigned int y = rand() % get_framebuffer_height(); + + const unsigned int offset = ( get_framebuffer_width() * 4 * y ) + x * 4; + + set_framebuffer( + offset, + uwe::Colour{ + static_cast<uint8_t>(rand() % 256), + static_cast<uint8_t>(rand() % 256), + static_cast<uint8_t>(rand() % 256), + SDL_ALPHA_OPAQUE}); + } +} + + +int main( int argc, char** argv ) { + uwe::App* app = new MyApp{width, height}; + + //app->dump_renderer_info(); + app->run(); return 0; } \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9efeb8327d5a91c833d30a1baf88175a2309d78 --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,85 @@ +/** + * @file + * @author Benedict R. Gaster + */ + +#include <iostream> +#include <iomanip> +#include <cstring> + +#include <app.hpp> + +namespace uwe { + + App::App() : + output_framerate_{false} { + } + + App::~App() { + + } + + void App::init(int width, int height) { + context_.init(width, height); + } + + void App::run() { + SDL_Event event; + bool running = true; + + unsigned int frames = 0; + Uint64 start = SDL_GetPerformanceCounter(); + + begin(); + + while( running ) + { + while( SDL_PollEvent( &event ) ) { + if( ( SDL_QUIT == event.type ) || + ( SDL_KEYDOWN == event.type && SDL_SCANCODE_ESCAPE == event.key.keysym.scancode ) ) { + running = false; + break; + } + else if (event.type == SDL_KEYDOWN) { + key_pressed(static_cast<Scancode>(event.key.keysym.scancode), event.key.repeat != 0); + } + else if (event.type == SDL_KEYUP) { + key_released(static_cast<Scancode>(event.key.keysym.scancode)); + } + } + + update(); + + draw(); + + // copy framebuffer pixels to GU texture for rendering + SDL_UpdateTexture( + context_.get_texture(), + NULL, + context_.get_pixels().data(), + context_.get_tex_width() * 4); + + SDL_RenderCopy( context_.get_renderer(), context_.get_texture(), NULL, NULL ); + SDL_RenderPresent( context_.get_renderer() ); + + frames++; + if (output_framerate_) { + const uint64_t end = SDL_GetPerformanceCounter(); + const static uint64_t freq = SDL_GetPerformanceFrequency(); + const double seconds = ( end - start ) / static_cast< double >( freq ); + if( seconds > 2.0 ) + { + std::cout + << frames << " frames in " + << std::setprecision(1) << std::fixed << seconds << " seconds = " + << std::setprecision(1) << std::fixed << frames / seconds << " FPS (" + << std::setprecision(3) << std::fixed << ( seconds * 1000.0 ) / frames << " ms/frame)" + << std::endl; + start = end; + frames = 0; + } + } + } + + } +} // namespace uwe \ No newline at end of file diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..035143cb7a79752f23a70cf153a194d4958f68a9 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,56 @@ +#include <iostream> + +#include <context.hpp> +#include <iomanip> +#include <cstring> + +namespace uwe { + Context::Context() { + } + + void Context::init(int width, int height) { + width_ = width; + height_ = height; + tex_width_ = width; + tex_height_ = height; + pixels_ = std::vector<uint8_t>(tex_width_ * tex_height_ *4), + SDL_Init( SDL_INIT_EVERYTHING ); + + window_ = SDL_CreateWindow( + "SDL2", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width_, height_, + SDL_WINDOW_SHOWN); + + renderer_ = SDL_CreateRenderer( + window_, + -1, + SDL_RENDERER_ACCELERATED); + + texture_ = SDL_CreateTexture( + renderer_, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + tex_width_, + tex_height_); + } + + Context::~Context() { + SDL_DestroyRenderer( renderer_ ); + SDL_DestroyWindow( window_ ); + SDL_Quit(); + } + + // void Context::run() { + + + void Context::dump_renderer_info() { + SDL_RendererInfo info; + SDL_GetRendererInfo( renderer_, &info ); + std::cout << "Renderer name: " << info.name << std::endl; + std::cout << "Texture formats: " << std::endl; + for( uint32_t i = 0; i < info.num_texture_formats; i++ ) { + std::cout << SDL_GetPixelFormatName( info.texture_formats[i] ) << std::endl; + } + } +} \ No newline at end of file diff --git a/src/graphics.cpp b/src/graphics.cpp deleted file mode 100644 index e4501d16deab17e2369078461c22be09f1de78af..0000000000000000000000000000000000000000 --- a/src/graphics.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include <iostream> - -#include <graphics.hpp> -#include <iomanip> -#include <cstring> - -namespace uwe { - - Context::Context(int width, int height) : - width_{width}, - height_{height}, - tex_width_{width}, - tex_height_{height}, - pixels_(tex_width_ * tex_height_ * 4, 0) { - SDL_Init( SDL_INIT_EVERYTHING ); - - window_ = SDL_CreateWindow( - "SDL2", - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - width_, height_, - SDL_WINDOW_SHOWN); - - renderer_ = SDL_CreateRenderer( - window_, - -1, - SDL_RENDERER_ACCELERATED); - - texture_ = SDL_CreateTexture( - renderer_, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - tex_width_, - tex_height_); - } - - Context::~Context() { - SDL_DestroyRenderer( renderer_ ); - SDL_DestroyWindow( window_ ); - SDL_Quit(); - } - - void Context::run() { - SDL_Event event; - bool running = true; - bool useLocktexture = false; - - unsigned int frames = 0; - Uint64 start = SDL_GetPerformanceCounter(); - - while( running ) - { - - SDL_SetRenderDrawColor( renderer_, 0, 0, 0, SDL_ALPHA_OPAQUE ); - SDL_RenderClear( renderer_ ); - - while( SDL_PollEvent( &event ) ) { - if( ( SDL_QUIT == event.type ) || - ( SDL_KEYDOWN == event.type && SDL_SCANCODE_ESCAPE == event.key.keysym.scancode ) ) - { - running = false; - break; - } - if( SDL_KEYDOWN == event.type && SDL_SCANCODE_L == event.key.keysym.scancode ) - { - useLocktexture = !useLocktexture; - std::cout << "Using " << ( useLocktexture ? "SDL_LockTexture() + memcpy()" : "SDL_UpdateTexture()" ) << std::endl; - } - } - - // splat down some random pixels - for( unsigned int i = 0; i < 1000; i++ ) { - const unsigned int x = rand() % tex_width_; - const unsigned int y = rand() % tex_height_; - - const unsigned int offset = ( tex_width_ * 4 * y ) + x * 4; - pixels_[ offset + 0 ] = rand() % 256; // b - pixels_[ offset + 1 ] = rand() % 256; // g - pixels_[ offset + 2 ] = rand() % 256; // r - pixels_[ offset + 3 ] = SDL_ALPHA_OPAQUE; // a - } - - if( useLocktexture ) { - unsigned char* lockedPixels = nullptr; - int pitch = 0; - SDL_LockTexture( - texture_, - NULL, - reinterpret_cast< void** >( &lockedPixels ), - &pitch - ); - std::memcpy( lockedPixels, pixels_.data(), pixels_.size() ); - SDL_UnlockTexture( texture_ ); - } - else { - SDL_UpdateTexture( - texture_, - NULL, - pixels_.data(), - tex_width_ * 4); - } - - SDL_RenderCopy( renderer_, texture_, NULL, NULL ); - SDL_RenderPresent( renderer_ ); - - frames++; - const uint64_t end = SDL_GetPerformanceCounter(); - const static uint64_t freq = SDL_GetPerformanceFrequency(); - const double seconds = ( end - start ) / static_cast< double >( freq ); - if( seconds > 2.0 ) - { - std::cout - << frames << " frames in " - << std::setprecision(1) << std::fixed << seconds << " seconds = " - << std::setprecision(1) << std::fixed << frames / seconds << " FPS (" - << std::setprecision(3) << std::fixed << ( seconds * 1000.0 ) / frames << " ms/frame)" - << std::endl; - start = end; - frames = 0; - } - } - - } - - void Context::dump_renderer_info() { - SDL_RendererInfo info; - SDL_GetRendererInfo( renderer_, &info ); - std::cout << "Renderer name: " << info.name << std::endl; - std::cout << "Texture formats: " << std::endl; - for( uint32_t i = 0; i < info.num_texture_formats; i++ ) { - std::cout << SDL_GetPixelFormatName( info.texture_formats[i] ) << std::endl; - } - } -} \ No newline at end of file