You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
448 lines
11 KiB
448 lines
11 KiB
/******************************************************************************* |
|
* TODO: |
|
* - renderer |
|
* - clean up main(), split out initialization and scene loading to new functions |
|
* - move input handling out to new file, controller? |
|
* - check over renderer, camera, gooey init after changes |
|
* - pass in frame time to camera movement functions to decouple speed from framerate |
|
* - 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 |
|
* - test YAML for scene loading https://yaml.org/ |
|
* - 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 <vector> |
|
|
|
#if defined(_WIN32) |
|
#include <windows.h> |
|
#include <Winuser.h> |
|
#include <SDL.h> |
|
#else |
|
#include <SDL2/SDL.h> |
|
#endif |
|
|
|
#include <SDL_image.h> |
|
|
|
// 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<Point> 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 = gooProcessEvent(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 |
|
renFreeBuffers(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::SinkOutputDebugString>(AixLog::Severity::trace, AixLog::Type::normal); |
|
#else |
|
AixLog::Log::init<AixLog::SinkCout>(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<hex_info>; |
|
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 (!renInit(g_render_state)) { |
|
LOG(ERROR) << "Unable to initialize graphics, exiting\n"; |
|
return 1; |
|
} |
|
|
|
if (!gooInit(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 (!renAddTexture(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 (!renCreateScene(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); |
|
|
|
renRenderFrame(r, g->grid.hex_array, g->entities, g->entity_count); |
|
|
|
if (r->is_debug_draw && g->grid.draw_mode == CONE_FILL) |
|
renRenderDebug(r, g_polygon_select_vertices); |
|
|
|
gooRender(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; |
|
} |
|
|
|
|