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

/*******************************************************************************
* 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;
}