spacebox/src/Game.cpp

582 lines
16 KiB
C++

#include "Game.hpp"
FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent)
{
get_delegate().subscribe(&FramerateIndicator::respond, this);
hide();
}
void FramerateIndicator::respond(SDL_Event& event)
{
if (get_delegate().compare(event, "toggle-framerate"))
{
toggle_hidden();
}
}
SDL_Surface* FramerateIndicator::get_surface()
{
std::string padded = sfw::pad(get_root()->frame_count_this_second, 2);
SDL_Surface* shaded = TTF_RenderText_Shaded(
get_root()->bp_mono_font, padded.c_str(), {0, 0, 0}, {255, 255, 255});
// SDL_Surface* converted = SDL_ConvertSurfaceFormat(
// shaded, SDL_PIXELFORMAT_ARGB8888, 0);
// SDL_Surface* flipped = zoomSurface(converted, 1, -1, SMOOTHING_OFF);
// SDL_FreeSurface(shaded);
// SDL_FreeSurface(converted);
if (!shaded)
{
get_root()->print_sdl_error("Could not create text");
}
return shaded;
}
void FramerateIndicator::refresh()
{
if (!is_hidden() && get_root()->bp_mono_font != NULL)
{
unload();
SDL_Surface* surface = get_surface();
SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface);
add_frame(texture);
SDL_FreeSurface(surface);
box.set_ne(get_display().get_window_box().get_ne());
}
}
Game::Game()
{
frame_length_history.reserve(5000);
set_framerate(get_configuration()["display"]["framerate"]);
delegate.subscribe(&Game::handle_quit_event, this, SDL_QUIT);
SDL_Log("GLEW %s", glewGetString(GLEW_VERSION));
putenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN=0");
// putenv("SDL_VIDEO_CENTERED=0");
SDL_version version;
SDL_GetVersion(&version);
SDL_Log("SDL %d.%d.%d", version.major, version.minor, version.patch);
fprintf(stderr, "stderr test message\n");
SDL_SetMainReady();
// SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
print_gl_attributes();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
{
print_sdl_error("SDL could not initialize");
flag_to_end();
}
// SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
print_gl_attributes();
std::vector<int> window_size = get_configuration()["display"]["dimensions"];
window = SDL_CreateWindow(
get_configuration()["display"]["title"].get_ref<const std::string&>().c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, window_size[0], window_size[1], SDL_WINDOW_OPENGL);
if (window == NULL)
{
print_sdl_error("Could not create window");
flag_to_end();
}
SDL_ShowCursor(0);
if (TTF_Init() < 0)
{
print_sdl_error("Could not initialize SDL ttf");
flag_to_end();
}
else
{
SDL_Log("initialized SDL ttf %d.%d.%d", SDL_TTF_MAJOR_VERSION,
SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL);
}
if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == NULL)
{
print_error("Could not load BPmono.ttf");
}
#if !defined(__EMSCRIPTEN__)
if (Mix_Init(MIX_INIT_FLAC) == 0)
{
print_sdl_error("Could not initialize SDL mixer");
flag_to_end();
}
else
{
SDL_Log("initialized SDL mixer %d.%d.%d", SDL_MIXER_MAJOR_VERSION,
SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL);
}
#endif
if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT,
MIX_DEFAULT_CHANNELS, 1024) < 0)
{
print_sdl_error("Could not set up audio");
}
SDL_Log("Using audio driver: %s", SDL_GetCurrentAudioDriver());
const int audio_device_count = SDL_GetNumAudioDevices(SDL_TRUE);
for (int ii = 0; ii < audio_device_count; ii++)
{
SDL_Log("Found audio capture device %i: %s", ii,
SDL_GetAudioDeviceName(ii, SDL_TRUE));
}
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_Log("big endian");
#else
SDL_Log("little endian");
#endif
last_frame_timestamp = SDL_GetTicks();
}
Game::~Game()
{
get_delegate().unsubscribe(this);
}
void Game::print_error(std::string message)
{
std::cerr << message << std::endl;
}
void Game::print_sdl_error(std::string message)
{
std::cerr << message << " " << SDL_GetError() << std::endl;
}
void Game::print_gl_attributes()
{
int major, minor;
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
SDL_Log("GL CONTEXT: %i, %i", major, minor);
int r, g, b, a, buf;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b);
SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a);
SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &buf);
SDL_Log("GL PIXELS: red: %i green: %i blue: %i alpha: %i", r, g, b, a);
}
void Game::print_frame_length_history()
{
for (float& frame_length : frame_length_history)
{
std::cout << frame_length << ", ";
}
std::cout << std::endl;
}
void Game::load_sdl_context()
{
if (glcontext != NULL)
{
SDL_GL_DeleteContext(glcontext);
glcontext = NULL;
}
if ((renderer = SDL_CreateRenderer(window, -1, 0)) == NULL)
{
print_sdl_error("Could not create renderer");
flag_to_end();
}
else
{
SDL_RendererInfo info;
SDL_GetRendererInfo(renderer, &info);
SDL_Log("renderer name: %s, flags: %i, texture formats: %i, "
"max texture w: %i, max texture h: %i", info.name, info.flags,
info.num_texture_formats, info.max_texture_width,
info.max_texture_height);
}
is_gl_context = false;
log_display_mode();
}
void Game::load_gl_context()
{
if (renderer != NULL)
{
SDL_DestroyRenderer(renderer);
renderer = NULL;
}
// SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
print_gl_attributes();
if ((glcontext = SDL_GL_CreateContext(window)) == NULL)
{
print_sdl_error("Could not get GL context");
flag_to_end();
}
GLenum error = glewInit();
if (error != GLEW_OK)
{
std::ostringstream message;
message << "GLEW could not initialize " << glewGetErrorString(error);
print_error(message.str());
}
SDL_Log("OpenGL %s, renderer %s, shading language %s",
glGetString(GL_VERSION), glGetString(GL_RENDERER),
glGetString(GL_SHADING_LANGUAGE_VERSION));
is_gl_context = true;
log_display_mode();
}
bool Game::log_gl_errors(std::string suffix)
{
GLenum error;
bool error_logged = false;
while ((error = glGetError()) != GL_NO_ERROR)
{
error_logged = true;
if (error == GL_INVALID_ENUM)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_INVALID_ENUM, an unacceptable value is specified for an "
"enumerated argument %s", suffix.c_str());
}
else if (error == GL_INVALID_VALUE)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_INVALID_VALUE, a numeric argument is out of range %s", suffix.c_str());
}
else if (error == GL_INVALID_OPERATION)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_INVALID_OPERATION, the specified operation is not allowed "
"in the current state %s", suffix.c_str());
}
else if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_INVALID_FRAMEBUFFER_OPERATION, the framebuffer object is "
"not complete %s", suffix.c_str());
}
else if (error == GL_OUT_OF_MEMORY)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_OUT_OF_MEMORY, there is not enough memory left to execute "
"the command %s", suffix.c_str());
}
else if (error == GL_STACK_UNDERFLOW)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_STACK_UNDERFLOW, an attempt has been made to perform an "
"operation that would cause an internal stack to underflow. %s", suffix.c_str());
}
else if (error == GL_STACK_OVERFLOW)
{
SDL_LogError(
SDL_LOG_CATEGORY_APPLICATION,
"GL_STACK_OVERFLOW, an attempt has been made to perform an "
"operation that would cause an internal stack to overflow. %s", suffix.c_str());
}
}
return error_logged;
}
// from SDL_GetCurrentDisplayMode.html
void Game::log_display_mode()
{
SDL_DisplayMode current;
for (int ii = 0; ii < SDL_GetNumVideoDisplays(); ii++)
{
int mode = SDL_GetCurrentDisplayMode(ii, &current);
if (mode != 0)
{
SDL_Log("Could not get display mode for video display #%d: %s", ii,
SDL_GetError());
}
else
{
SDL_Log("Display #%d: display mode is %dx%dpx @ %dhz %s", ii,
current.w, current.h, current.refresh_rate,
get_pixel_format_string(current.format).c_str());
}
}
}
void Game::log_surface_format(SDL_Surface* surface, std::string preface)
{
SDL_PixelFormat* format = surface->format;
std::string pixel_format = get_pixel_format_string(format->format);
SDL_Log("%s bpp: %i mask: %i %i %i %i format: %s", preface.c_str(),
format->BytesPerPixel, format->Rmask, format->Gmask, format->Bmask,
format->Amask, pixel_format.c_str());
}
std::string Game::get_pixel_format_string(Uint32 format)
{
std::string pixel_format;
if (format == SDL_PIXELFORMAT_UNKNOWN)
{
pixel_format = "SDL_PIXELFORMAT_UNKNOWN";
}
else if (format == SDL_PIXELFORMAT_INDEX1LSB)
{
pixel_format = "SDL_PIXELFORMAT_INDEX1LSB";
}
else if (format == SDL_PIXELFORMAT_INDEX1MSB)
{
pixel_format = "SDL_PIXELFORMAT_INDEX1MSB";
}
else if (format == SDL_PIXELFORMAT_INDEX4LSB)
{
pixel_format = "SDL_PIXELFORMAT_INDEX4LSB";
}
else if (format == SDL_PIXELFORMAT_INDEX4MSB)
{
pixel_format = "SDL_PIXELFORMAT_INDEX4MSB";
}
else if (format == SDL_PIXELFORMAT_INDEX8)
{
pixel_format = "SDL_PIXELFORMAT_INDEX8";
}
else if (format == SDL_PIXELFORMAT_RGB332)
{
pixel_format = "SDL_PIXELFORMAT_RGB332";
}
else if (format == SDL_PIXELFORMAT_RGB444)
{
pixel_format = "SDL_PIXELFORMAT_RGB444";
}
else if (format == SDL_PIXELFORMAT_RGB555)
{
pixel_format = "SDL_PIXELFORMAT_RGB555";
}
else if (format == SDL_PIXELFORMAT_BGR555)
{
pixel_format = "SDL_PIXELFORMAT_BGR555";
}
else if (format == SDL_PIXELFORMAT_ARGB4444)
{
pixel_format = "SDL_PIXELFORMAT_ARGB4444";
}
else if (format == SDL_PIXELFORMAT_RGBA4444)
{
pixel_format = "SDL_PIXELFORMAT_RGBA4444";
}
else if (format == SDL_PIXELFORMAT_ABGR4444)
{
pixel_format = "SDL_PIXELFORMAT_ABGR4444";
}
else if (format == SDL_PIXELFORMAT_BGRA4444)
{
pixel_format = "SDL_PIXELFORMAT_BGRA4444";
}
else if (format == SDL_PIXELFORMAT_ARGB1555)
{
pixel_format = "SDL_PIXELFORMAT_ARGB1555";
}
else if (format == SDL_PIXELFORMAT_RGBA5551)
{
pixel_format = "SDL_PIXELFORMAT_RGBA5551";
}
else if (format == SDL_PIXELFORMAT_ABGR1555)
{
pixel_format = "SDL_PIXELFORMAT_ABGR1555";
}
else if (format == SDL_PIXELFORMAT_BGRA5551)
{
pixel_format = "SDL_PIXELFORMAT_BGRA5551";
}
else if (format == SDL_PIXELFORMAT_RGB565)
{
pixel_format = "SDL_PIXELFORMAT_RGB565";
}
else if (format == SDL_PIXELFORMAT_BGR565)
{
pixel_format = "SDL_PIXELFORMAT_BGR565";
}
else if (format == SDL_PIXELFORMAT_RGB24)
{
pixel_format = "SDL_PIXELFORMAT_RGB24";
}
else if (format == SDL_PIXELFORMAT_BGR24)
{
pixel_format = "SDL_PIXELFORMAT_BGR24";
}
else if (format == SDL_PIXELFORMAT_RGB888)
{
pixel_format = "SDL_PIXELFORMAT_RGB888";
}
else if (format == SDL_PIXELFORMAT_RGBX8888)
{
pixel_format = "SDL_PIXELFORMAT_RGBX8888";
}
else if (format == SDL_PIXELFORMAT_BGR888)
{
pixel_format = "SDL_PIXELFORMAT_BGR888";
}
else if (format == SDL_PIXELFORMAT_BGRX8888)
{
pixel_format = "SDL_PIXELFORMAT_BGRX8888";
}
else if (format == SDL_PIXELFORMAT_ARGB8888)
{
pixel_format = "SDL_PIXELFORMAT_ARGB8888";
}
else if (format == SDL_PIXELFORMAT_RGBA8888)
{
pixel_format = "SDL_PIXELFORMAT_RGBA8888";
}
else if (format == SDL_PIXELFORMAT_ABGR8888)
{
pixel_format = "SDL_PIXELFORMAT_ABGR8888";
}
else if (format == SDL_PIXELFORMAT_BGRA8888)
{
pixel_format = "SDL_PIXELFORMAT_BGRA8888";
}
else if (format == SDL_PIXELFORMAT_ARGB2101010)
{
pixel_format = "SDL_PIXELFORMAT_ARGB2101010";
}
return pixel_format;
}
SDL_Window* Game::get_window()
{
return window;
}
SDL_Renderer* Game::get_renderer()
{
return renderer;
}
void Game::run()
{
#if defined(__EMSCRIPTEN__)
SDL_Log("using emscripten main loop");
emscripten_set_main_loop_arg(&loop, this, -1, true);
#else
SDL_Log("using standard main loop");
while (not done)
{
frame(SDL_GetTicks());
SDL_Delay(8);
}
#endif
}
void Game::frame(float ticks)
{
// std::cout << "frame_length: " << frame_length << ", ticks: " << ticks << ", last_frame_timestamp: " << last_frame_timestamp <<
// ", frame_time_overflow: " << frame_time_overflow;
if (ticks - last_frame_timestamp + frame_time_overflow >= frame_length)
{
last_frame_length = ticks - last_frame_timestamp;
if (frame_length_history.size() == 5000)
{
frame_length_history.pop_back();
}
frame_length_history.insert(frame_length_history.begin(), last_frame_length);
frame_time_overflow = last_frame_length + frame_time_overflow - frame_length;
last_frame_timestamp = ticks;
// std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]";
recorder.update();
delegate.dispatch();
update();
framerate_indicator.update();
if (!is_gl_context)
{
SDL_RenderPresent(renderer);
}
if (frame_time_overflow > frame_length)
{
// SDL_Log("%i frame(s) dropped", ((int) (frame_time_overflow / frame_length)));
frame_time_overflow = 0;
}
frame_count_this_second++;
if (ticks - last_frame_count_timestamp >= 1000)
{
framerate_indicator.refresh();
last_frame_count_timestamp = ticks;
frame_count_this_second = 0;
}
}
// std::cout << std::endl;
}
#if defined(__EMSCRIPTEN__)
void loop(void* context)
{
Game* game = static_cast<Game*>(context);
game->frame(emscripten_performance_now());
if (game->done)
{
emscripten_cancel_main_loop();
}
}
#endif
void Game::flag_to_end()
{
done = true;
}
glm::vec2 Game::weight(glm::vec2 motion)
{
return glm::vec2(weight(motion.x), weight(motion.y));
}
void Game::set_framerate(int f)
{
if (f < 1)
{
f = 1;
}
framerate = f;
frame_length = 1000.0 / framerate;
}
void Game::handle_quit_event(SDL_Event &event)
{
if (event.type == SDL_QUIT)
{
flag_to_end();
}
}
void Game::quit()
{
if (glcontext != NULL)
{
SDL_GL_DeleteContext(glcontext);
}
if (renderer != NULL)
{
SDL_DestroyRenderer(renderer);
}
if (window != NULL)
{
SDL_DestroyWindow(window);
}
if (TTF_WasInit())
{
TTF_CloseFont(bp_mono_font);
TTF_Quit();
}
Mix_CloseAudio();
Mix_Quit();
SDL_Quit();
}