// Some defaults for the game layout #define DEBUG_DRAW true #define VIEWPORT_WIDTH 1920 #define VIEWPORT_HEIGHT 1080 #define CONE_ANGLE 30 #define VSYNC_ENABLED true #define DATA_DIR "../data" #define DEFAULT_SCENE_FILE "hashmap_scene.json" #define SCENE_SCHEMA_FILE "scene_schema.json" #define MAX_ENTITIES 1000 #include #if defined(_WIN32) #include #include #include #else #include #endif // TODO: replace aixlog with simpler logging #if defined(_WIN32) #pragma warning(push) #pragma warning(disable : 4003) #endif #include "aixlog.hpp" #include "gooey.h" #include "hexgame.h" #include "mesh.h" #include "platform_wait_for_vblank.h" #include "renderer.h" #include "scene_loader.h" #include "util.h" using std::vector; static game_state* g_game_state; static render_state* g_render_state; static vector g_polygon_select_vertices = {Point(), Point(), Point(), Point()}; bool loadSceneFromJson(game_state* s, render_state* rs) { slSceneDoc* sd = slLoadFile(DATA_DIR, DEFAULT_SCENE_FILE, SCENE_SCHEMA_FILE); if (sd != nullptr) { game_state* gs = g_game_state; render_state* rs = g_render_state; if (!slParseEntities(sd, gs->entities, gs->entity_count, MAX_ENTITIES, DATA_DIR)) { LOG(ERROR) << "Error loading Entities, exiting\n"; slFreeSceneDoc(sd); return false; } slParseCamera(sd, rs->cam); slParseHexGrid(sd, gs->grid, rs->palette_image); if (slCreateHexRenderGroups(gs->grid, rs)) { if (!hgInit(gs->grid, rs->filled_hex_render_group, rs->hex_line_render_group, DATA_DIR)) { slFreeSceneDoc(sd); return false; } } else { LOG(ERROR) << "Error creating hexgrid render groups\n"; slFreeSceneDoc(sd); return false; } if (!slParseLights(sd, rs->lights, rs->num_lights, rs->max_lights)) { LOG(ERROR) << "Error loading lights, exiting\n"; slFreeSceneDoc(sd); return false; } } else { return false; } slFreeSceneDoc(sd); return true; } bool init() { // init global game state g_game_state = UTIL_ALLOC(1, game_state); g_game_state->entities = UTIL_ALLOC(MAX_ENTITIES, Entity); // TODO: I think this is necessary because we're using calloc to initialize gamestate // which contains the hashtable. g_game_state->grid.hex_map = std::unordered_map(); // NOTE: testing hex_add g_game_state->grid.draw_mode = ADD_HEXES; // TODO: maybe add this as an option to scene json? g_game_state->grid.normal = v3f(0.f, 0.f, 1.f); // init global render state g_render_state = UTIL_ALLOC(1, render_state); g_render_state->is_debug_draw = DEBUG_DRAW; g_render_state->viewport_dims = v2i(VIEWPORT_WIDTH, VIEWPORT_HEIGHT); if (SDL_Init(SDL_INIT_VIDEO) != 0) { LOG(ERROR) << "Error, SDL_Init: " << SDL_GetError() << "\n"; return false; } if (!renInit(g_render_state)) { LOG(ERROR) << "Unable to initialize graphics, exiting\n"; return false; } if (!gooInit(g_render_state->handles, g_render_state->viewport_dims)) { LOG(ERROR) << "Fooey, No Gooey!\n"; return false; } if (!meInitAssimp()) { LOG(ERROR) << "Error initializing assimp\n"; return false; } if (!loadSceneFromJson(g_game_state, g_render_state)) { LOG(ERROR) << "Error loading scene\n"; return false; } if (!renCreateScene(g_render_state, g_game_state->entities, g_game_state->entity_count)) { LOG(ERROR) << "Error in vertex data, exiting\n"; return false; } #if defined(_WIN32) LOG(DEBUG) << "TODO: Test SDL_Delay frame timer in win32\n"; if (!platform_init(g_render_state->handles.window)) { LOG(ERROR) << "Couldn't get SDL platform info, exiting\n"; return false; } #endif return true; } v2i mapMouseToViewport(int32 x, int32 y) { v2i coords; coords.x = x; coords.y = g_render_state->viewport_dims.y - y; return coords; } void startDrawHelper(int32 x, int32 y) { hgResetHexes(g_game_state->grid); v2i dims = g_render_state->viewport_dims; v2f v = cameraUnproject(g_render_state->cam, x, y, dims.x, dims.y); hex_info *hex = hgGetSingleHex(g_game_state->grid, v.x, v.y); if (hex) { hex->selected = true; g_game_state->grid.start_hex = g_game_state->grid.current_hex = hex; } } void handleMouseDown(SDL_MouseButtonEvent &e) { game_state* gs = g_game_state; render_state* rs = g_render_state; v2i coords = mapMouseToViewport(e.x, e.y); v2i dims = rs->viewport_dims; switch (gs->grid.draw_mode) { case FILL: startDrawHelper(coords.x, coords.y); break; case LINE: startDrawHelper(coords.x, coords.y); break; case CONE_FILL: startDrawHelper(coords.x, coords.y); break; case PATHFINDING: break; case ADD_HEXES: { v3f ray = cameraCreateRay(rs->cam, coords, dims); v3f xsect; if (cameraIntersectPlane(rs->cam, ray, gs->grid.position, gs->grid.normal, xsect)) hgAddHex(gs->grid, xsect, rs->filled_hex_render_group, rs->hex_line_render_group); break; } case REMOVE_HEXES: { v3f ray = cameraCreateRay(rs->cam, coords, dims); v3f xsect; if (cameraIntersectPlane(rs->cam, ray, gs->grid.position, gs->grid.normal, xsect)) hgRemoveHex(gs->grid, xsect, rs->filled_hex_render_group, rs->hex_line_render_group); break; } case NONE: default: v2f v = cameraUnproject(rs->cam, coords.x, coords.y, dims.x, dims.y); hex_info *hxi = hgGetSingleHex(gs->grid, v.x, v.y); if (hxi) { hxi->selected = !hxi->selected; gs->grid.start_hex = gs->grid.current_hex = hxi; } break; } } void handleMouseMove(SDL_MouseMotionEvent &e) { game_state* gs = g_game_state; render_state* rs = g_render_state; v2i coords = mapMouseToViewport(e.x, e.y); v2i dims = rs->viewport_dims; v2f v = cameraUnproject(rs->cam, coords.x, coords.y, dims.x, dims.y); switch (gs->grid.draw_mode) { case FILL: hgUpdateHexFill(gs->grid, v.x, v.y); break; case LINE: hgUpdateHexLineDraw(gs->grid, v.x, v.y); break; case CONE_FILL: hgUpdateHexConeFill(gs->grid, v.x, v.y, CONE_ANGLE, g_polygon_select_vertices); break; case PATHFINDING: break; case ADD_HEXES: { if (gs->grid.is_selecting) { v3f ray = cameraCreateRay(rs->cam, coords, dims); v3f xsect; if (cameraIntersectPlane(rs->cam, ray, gs->grid.position, gs->grid.normal, xsect)) hgAddHex(gs->grid, xsect, rs->filled_hex_render_group, rs->hex_line_render_group); } break; } case REMOVE_HEXES: { if (gs->grid.is_selecting) { v3f ray = cameraCreateRay(rs->cam, coords, dims); v3f xsect; if (cameraIntersectPlane(rs->cam, ray, gs->grid.position, gs->grid.normal, xsect)) hgRemoveHex(gs->grid, xsect, rs->filled_hex_render_group, rs->hex_line_render_group); } break; } case NONE: default: break; } } bool processSDLEvents() { SDL_Event e; bool run = true; while (SDL_PollEvent(&e)) { // let gooey have event // TODO: need to check for both io.WantCaptureKeyboard and io.WantCaptureMouse // to fix bug with 'ESC' not passing through while in imgui bool gooey_wants = gooProcessEvent(e); game_state* gs = g_game_state; switch (e.type) { case SDL_QUIT: run = false; break; case SDL_KEYDOWN: switch (e.key.keysym.sym) { case SDLK_ESCAPE: run = false; break; case SDLK_e: gs->is_moveforward = true; break; case SDLK_s: gs->is_moveleft = true; break; case SDLK_d: gs->is_movebackward = true; break; case SDLK_f: gs->is_moveright = true; break; case SDLK_SPACE: gs->is_moveup = true; break; case SDLK_c: gs->is_movedown = true; break; case SDLK_r: gs->is_rotateCW = true; break; case SDLK_w: gs->is_rotateCCW = true; break; } break; case SDL_KEYUP: switch (e.key.keysym.sym) { case SDLK_e: gs->is_moveforward = false; break; case SDLK_s: gs->is_moveleft = false; break; case SDLK_d: gs->is_movebackward = false; break; case SDLK_f: gs->is_moveright = false; break; case SDLK_SPACE: gs->is_moveup = false; break; case SDLK_c: gs->is_movedown = false; break; case SDLK_r: gs->is_rotateCW = false; break; case SDLK_w: gs->is_rotateCCW = false; break; } break; case SDL_MOUSEBUTTONDOWN: if (!gooey_wants) { if (e.button.button == SDL_BUTTON_LEFT) { gs->grid.is_selecting = true; handleMouseDown(e.button); } else if (e.button.button == SDL_BUTTON_RIGHT) { gs->is_camera_rotate = true; } } break; case SDL_MOUSEBUTTONUP: if (!gooey_wants) { if (e.button.button == SDL_BUTTON_LEFT) gs->grid.is_selecting = false; else if (e.button.button == SDL_BUTTON_RIGHT) gs->is_camera_rotate = false; } break; case SDL_MOUSEMOTION: if (!gooey_wants && gs->grid.is_selecting) handleMouseMove(e.motion); else if(!gooey_wants && gs->is_camera_rotate) cameraRotate(g_render_state->cam, e.motion.xrel, e.motion.yrel); break; default: break; } } return run; } bool cleanUp(SDL_Handles &handles) { gooFree(); renFreeBuffers(g_render_state); // renderer.h for (SDL_Surface *surface : handles.texSurfaces) SDL_FreeSurface(surface); meShutdownAssimp(); // mesh.h SDL_GL_DeleteContext(handles.glContext); SDL_DestroyWindow(handles.window); SDL_Quit(); game_state* gs = g_game_state; for (uint i = 0; i < gs->entity_count; i++) { meFreeMeshGroup(gs->entities[i].mesh_group); rgFree(gs->entities[i].ren_group); } utilSafeFree(gs->entities); utilSafeFree(g_render_state); utilSafeFree(g_game_state); return true; } #if defined(_WIN32) int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) #else int main(int argc, char* argv[]) #endif { #if defined(_WIN32) AixLog::Log::init(AixLog::Severity::trace, AixLog::Type::normal); #else AixLog::Log::init(AixLog::Severity::trace, AixLog::Type::normal); #endif LOG(INFO) << "Application started\n"; if (!init()) { LOG(ERROR) << "failed init, exiting\n"; return -1; } // main loop const uint TARGET_FPS = 60; const uint FRAME_DELAY = 1000 / TARGET_FPS; uint frameStart, frameTime; while (processSDLEvents()) { frameStart = SDL_GetTicks(); game_state* gs = g_game_state; render_state* rs = g_render_state; cameraMove(rs->cam, gs->is_moveup, gs->is_moveleft, gs->is_movedown, gs->is_moveright, gs->is_moveforward, gs->is_movebackward); renRenderFrame(rs, gs->grid, gs->entities, gs->entity_count); if (rs->is_debug_draw && gs->grid.draw_mode == CONE_FILL) renRenderDebug(rs, g_polygon_select_vertices); gooRender(gs, rs); SDL_GL_SwapWindow(rs->handles.window); // TODO: test SDL_Delay on Win32 //platform_wait_for_vblank(VSYNC_ENABLED); frameTime = SDL_GetTicks() - frameStart; if (FRAME_DELAY > frameTime) SDL_Delay(FRAME_DELAY - frameTime); } cleanUp(g_render_state->handles); LOG(INFO) << "Application exiting\n"; return 0; }