165 lines
4.8 KiB
C++
165 lines
4.8 KiB
C++
#include <memory>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <GL/glew.h>
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_keyboard.h>
|
|
#include <SDL3/SDL_timer.h>
|
|
|
|
#include "db/db.h"
|
|
|
|
#include "gfx/shape_renderer.h"
|
|
#include "gfx/txt_renderer.h"
|
|
#include "game_state.h"
|
|
#include "ui/ui_renderer.h"
|
|
#include "gfx/types.h"
|
|
#include "ui/cursor_manager.h"
|
|
#include "debug/debug_stats.h"
|
|
|
|
const int SCREEN_WIDTH = 1280;
|
|
const int SCREEN_HEIGHT = 720;
|
|
int main(int argc, char** argv) {
|
|
/* Init SDL. */
|
|
if(!SDL_Init(SDL_INIT_VIDEO)) {
|
|
printf("SDL could not initialise! SDL_ERROR: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
/* Set OpenGL attributes. */
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
/* Create a window. */
|
|
SDL_Window* window = SDL_CreateWindow(
|
|
"Bettola Client",
|
|
SCREEN_WIDTH,
|
|
SCREEN_HEIGHT,
|
|
SDL_WINDOW_OPENGL
|
|
);
|
|
|
|
if(window == NULL) {
|
|
printf("Unable to create window! SDL_ERROR: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
/* Create OpenGL context. */
|
|
SDL_GLContext context = SDL_GL_CreateContext(window);
|
|
if(context == NULL) {
|
|
printf("OpenGL context could not be created! SDL_ERROR: %s\n", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
/* Initialise GLEW. */
|
|
glewExperimental = GL_TRUE;
|
|
GLenum glewError = glewInit();
|
|
if (glewError != GLEW_OK) {
|
|
/* NOTE:
|
|
* Dear future self, or other intrepid developer:
|
|
* In some environments (like the Fedora VM I tested),
|
|
* glewInit() returns an error here even when the context is valid.
|
|
* This is a known quirk. We print the error but continue anyway,
|
|
* because it's not fatal. Don't "fix" this by making it exit. Thank you.
|
|
* It's GLEW, not you.
|
|
*/
|
|
fprintf(stderr, "Warning: glewInit failed with error code %u: %s. Attempting to continue anyway.\n",
|
|
glewError, glewGetErrorString(glewError));
|
|
}
|
|
|
|
/* Configure OpenGL. */
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
/* Listen for text input. */
|
|
SDL_StartTextInput(window);
|
|
|
|
/* Init cursor manager. */
|
|
CursorManager::init();
|
|
|
|
/* Init text renderer. */
|
|
TextRenderer* txt_render_instance = new TextRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
txt_render_instance->load_font("assets/fonts/hack/Hack-Regular.ttf", 14);
|
|
|
|
/* Init shape renderer. */
|
|
ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
|
|
/* Init UI renderer. */
|
|
UIRenderer* ui_renderer_instance = new UIRenderer(shape_renderer_instance,
|
|
txt_render_instance, SCREEN_HEIGHT);
|
|
|
|
auto game_state = std::make_unique<GameState>();
|
|
if(argc > 1 && std::string(argv[1]) == "-sp") {
|
|
game_state->start_single_player_now(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
} else {
|
|
game_state->init(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
}
|
|
|
|
/* timer for cursor blink. */
|
|
Uint32 last_blink_time = 0;
|
|
bool show_cursor = true;
|
|
|
|
/* Timestep. */
|
|
Uint64 last_frame_time = SDL_GetPerformanceCounter();
|
|
float dt = 0.0f;
|
|
|
|
bool running = true;
|
|
while(running) {
|
|
/* Reset per-frame stats. */
|
|
DebugStats::reset();
|
|
|
|
/* Event handling. */
|
|
SDL_Event event;
|
|
while(SDL_PollEvent(&event)) {
|
|
if(event.type == SDL_EVENT_QUIT) { running = false; }
|
|
game_state->handle_event(&event, SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
}
|
|
|
|
/* Calculate delta time. */
|
|
Uint64 current_frame_time = SDL_GetPerformanceCounter();
|
|
dt = (current_frame_time - last_frame_time) / (float)SDL_GetPerformanceFrequency();
|
|
last_frame_time = current_frame_time;
|
|
|
|
/* Clamp dt to avoid large jumps. */
|
|
if(dt > 0.1f) dt = 0.1f;
|
|
|
|
Uint32 current_time = SDL_GetTicks();
|
|
if(current_time - last_blink_time > 500) { /* Every 500ms. */
|
|
show_cursor = !show_cursor;
|
|
last_blink_time = current_time;
|
|
}
|
|
|
|
/* Clear screen. */
|
|
glClearColor(0.04f, 0.05f, 0.06, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
RenderContext context = {
|
|
ui_renderer_instance,
|
|
SCREEN_HEIGHT,
|
|
show_cursor
|
|
};
|
|
game_state->render(context);
|
|
|
|
/* Update game state after rendering so stats are for the frame just rendered.
|
|
* I know this is unusual, but given the text based nature of the game
|
|
* we won't see a negative impact, and this is better than a large refactor ;)
|
|
*/
|
|
game_state->update(dt, DebugStats::draw_calls, DebugStats::shape_vertices,
|
|
DebugStats::text_vertices);
|
|
|
|
/* It's really odd to call it SwapWindow now, rather than SwapBuffer. */
|
|
SDL_GL_SwapWindow(window);
|
|
}
|
|
|
|
/* Cleanup. */
|
|
game_state.reset();
|
|
delete ui_renderer_instance;
|
|
delete shape_renderer_instance;
|
|
delete txt_render_instance;
|
|
SDL_GL_DestroyContext(context);
|
|
CursorManager::quit();
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
return 0;
|
|
}
|