/******************************************************************************* * TODO: * - renderer * - add prefix to interface function names for eg) gooey.h, renderer.h * - clean up main(), split out initialization and scene loading to new functions * - check over renderer, camera, gooey init after changes * - pass in frame time to camera movement functions to decouple speed from framerate * - attempt to fix bug with starting camera xform * - test camera speed when moving with composite vector * - may be fixed?? need to test by examining camera position between frames * and compare the distance * - add 3d orientation widget to gooey * * - implement texturing to reduce the number of meshes generated by assimp * from materials (currently creates a sepereate mesh per material) * - need to do UV mapping on models, and parse UV data in shader * - map generation * - pathfinding * - assimp animation * - replace aixlog with custom logging iostream? * - think about combining mesh data and render_group data to save some memory * - actually don't need to save the vertex/normal buffers after passing * to opengl * - use a storage pool for assimp meshes allowing reuse across entities * - replace remaining calls to malloc/free with safe(r) function in util.h * - add cpu perforcmace counters in render loop * - maybe try deferred rendering at some point to use a ton of lights * - check for memory leaks w/ valgrind ******************************************************************************/ // 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 // TODO: testing scene_loader #define DATA_DIR "../data" #define DEFAULT_SCENE_FILE "test_scene.json" #define SCENE_SCHEMA_FILE "scene_schema.json" #define MAX_ENTITIES 1000 #include #if defined(_WIN32) #include #include #include #else #include #endif #include // 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()}; v2i mapMouseToViewport(int32 x, int32 y) { v2i coords; coords.x = x; coords.y = g_render_state->viewport_dims.y - y; return coords; } void setStartHex(hex_info *hex) { hex->selected = true; hex->current_color = g_game_state->grid.selected_fill_color; g_game_state->grid.start_hex = g_game_state->grid.current_hex = hex; g_game_state->grid.is_selecting = true; } 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) setStartHex(hex); } void handleMouseDown(SDL_MouseButtonEvent &e) { v2i coords = mapMouseToViewport(e.x, e.y); switch (g_game_state->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 NONE: // fall through default: { v2i dims = g_render_state->viewport_dims; v2f v = cameraUnproject(g_render_state->cam, coords.x, coords.y, dims.x, dims.y); hex_info *hex = hgGetSingleHex(g_game_state->grid, v.x, v.y); if (hex) { hex->selected = !hex->selected; if (hex->selected) { hex->current_color = g_game_state->grid.selected_fill_color; } else { hex->current_color = hex->stored_color; } } }break; } } void handleMouseMove(SDL_MouseMotionEvent &e) { v2i coords = mapMouseToViewport(e.x, e.y); v2i dims = g_render_state->viewport_dims; v2f v = cameraUnproject(g_render_state->cam, coords.x, coords.y, dims.x, dims.y); switch (g_game_state->grid.draw_mode) { case FILL: hgUpdateHexFill(g_game_state->grid, v.x, v.y); break; case LINE: hgUpdateHexLineDraw(g_game_state->grid, v.x, v.y); break; case CONE_FILL: hgUpdateHexConeFill(g_game_state->grid, v.x, v.y, CONE_ANGLE, g_polygon_select_vertices); break; case PATHFINDING: break; case NONE: // fall through 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 = gooeyProcessEvent(e); game_state* g = 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: g->is_moveforward = true; break; case SDLK_s: g->is_moveleft = true; break; case SDLK_d: g->is_movebackward = true; break; case SDLK_f: g->is_moveright = true; break; case SDLK_SPACE: g->is_moveup = true; break; case SDLK_c: g->is_movedown = true; break; case SDLK_r: g->is_rotateCW = true; break; case SDLK_w: g->is_rotateCCW = true; break; } break; case SDL_KEYUP: switch (e.key.keysym.sym) { case SDLK_e: g->is_moveforward = false; break; case SDLK_s: g->is_moveleft = false; break; case SDLK_d: g->is_movebackward = false; break; case SDLK_f: g->is_moveright = false; break; case SDLK_SPACE: g->is_moveup = false; break; case SDLK_c: g->is_movedown = false; break; case SDLK_r: g->is_rotateCW = false; break; case SDLK_w: g->is_rotateCCW = false; break; } break; case SDL_MOUSEBUTTONDOWN: if (!gooey_wants) { if (e.button.button == SDL_BUTTON_LEFT) { g->grid.is_selecting = true; handleMouseDown(e.button); } else if (e.button.button == SDL_BUTTON_RIGHT) { g->is_camera_rotate = true; } } break; case SDL_MOUSEBUTTONUP: if (!gooey_wants) { if (e.button.button == SDL_BUTTON_LEFT) g->grid.is_selecting = false; else if (e.button.button == SDL_BUTTON_RIGHT) g->is_camera_rotate = false; } break; case SDL_MOUSEMOTION: if (!gooey_wants && g->grid.is_selecting) handleMouseMove(e.motion); else if(!gooey_wants && g->is_camera_rotate) cameraRotate(g_render_state->cam, e.motion.xrel, e.motion.yrel); break; default: break; } } return run; } bool cleanUp(SDL_Handles &handles) { #if 0 shutdownGooey(); #endif freeBuffers(g_render_state); // renderer.h for (SDL_Surface *surface : handles.texSurfaces) SDL_FreeSurface(surface); meShutdownAssimp(); // mesh.h IMG_Quit(); // SDL_image SDL_GL_DeleteContext(handles.glContext); SDL_DestroyWindow(handles.window); SDL_Quit(); game_state* g = g_game_state; if (g_game_state->grid.hex_array) delete g_game_state->grid.hex_array; for (uint i = 0; i < g->entity_count; i++) { meFreeMeshGroup(g->entities[i].mesh_group); rgFree(g->entities[i].ren_group); } utilSafeFree(g->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"; // init global game state g_game_state = UTIL_ALLOC(1, game_state); g_game_state->grid.hex_array = new vector; g_game_state->entities = UTIL_ALLOC(MAX_ENTITIES, Entity); // 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 1; } if (!initRenderer(g_render_state)) { LOG(ERROR) << "Unable to initialize graphics, exiting\n"; return 1; } if (!initGooey(g_render_state->handles, g_render_state->viewport_dims)) { LOG(ERROR) << "Fooey, No Gooey!\n"; return 1; } { // load support for the JPG and PNG image formats int flags=IMG_INIT_JPG|IMG_INIT_PNG; int initted=IMG_Init(flags); if((initted&flags) != flags) { LOG(ERROR) << "IMG_Init: Failed to init required jpg and png support!\n"; LOG(ERROR) << "IMG_Init: " << IMG_GetError() << "\n"; return 1; } // pre-load textures const char * path = "../data/coords.layout.flat.png"; if (!addTexture(g_render_state->handles, path)) { LOG(ERROR) << "Error adding " << path << "\n"; return 1; } } if (!meInitAssimp()) { LOG(ERROR) << "Error initializing assimp\n"; return 1; } // load scene from json slSceneDoc* sd = slLoadFile(DATA_DIR, DEFAULT_SCENE_FILE, SCENE_SCHEMA_FILE); if (sd != nullptr) { game_state* g = g_game_state; render_state* rs = g_render_state; if (!slParseEntities(sd, g->entities, g->entity_count, MAX_ENTITIES, DATA_DIR)) { LOG(ERROR) << "Error loading Entities, exiting\n"; return 1; } slParseCamera(sd, rs->cam); slParseHexGrid(sd, g->grid); rs->filled_hex_render_group = hgInitGLBuffers(g->grid, rs->default_shader, true); rs->hex_line_render_group = hgInitGLBuffers(g->grid, rs->default_shader, false); if ((rs->filled_hex_render_group == nullptr) || (rs->hex_line_render_group == nullptr)) { LOG(ERROR) << "Error allocating render_group, exiting\n"; return 1; } if (!slParseLights(sd, rs->lights, rs->num_lights, rs->max_lights)) { LOG(ERROR) << "Error loading lights, exiting\n"; return 1; } slFreeSceneDoc(sd); } else { LOG(ERROR) << "Error loading scene, exiting\n"; return 1; } if (!createScene(g_render_state, g_game_state->entities, g_game_state->entity_count)) { LOG(ERROR) << "Error in vertex data, exiting\n"; return 1; } #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 1; } #endif // main loop const uint TARGET_FPS = 60; const uint FRAME_DELAY = 1000 / TARGET_FPS; uint frameStart, frameTime; while (processSDLEvents()) { frameStart = SDL_GetTicks(); game_state* g = g_game_state; render_state* r = g_render_state; cameraMove(r->cam, g->is_moveup, g->is_moveleft, g->is_movedown, g->is_moveright, g->is_moveforward, g->is_movebackward); renderFrame(r, g->grid.hex_array, g->entities, g->entity_count); if (r->is_debug_draw && g->grid.draw_mode == CONE_FILL) renderDebug(r, g_polygon_select_vertices); renderGooey(r->handles, &g->grid, r->is_debug_draw, &r->cam); SDL_GL_SwapWindow(r->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; }