Browse Source

Initial commit

master
dummy 9 years ago
commit
60e1513c20
  1. 3
      .gitignore
  2. 3
      .gitmodules
  3. 1
      ext/imgui
  4. 13
      src/Makefile
  5. 24
      src/build.bat
  6. 441
      src/gooey.h
  7. 677
      src/hexgame.cpp
  8. 99
      src/hexgame.h
  9. 270
      src/hexlib.h
  10. 4061
      src/stb_truetype.h

3
.gitignore vendored

@ -0,0 +1,3 @@
bin/
build/

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "ext/imgui"]
path = ext/imgui
url = https://github.com/ocornut/imgui.git

1
ext/imgui

@ -0,0 +1 @@
Subproject commit 6a25a8720ac27573225643a1188d8a49b4ad614e

13
src/Makefile

@ -0,0 +1,13 @@
CXXFLAGS = -std=c++11 -g -Wall
INCLUDES =
LDFLAGS = -lSDL2 -lGLEW -lGL
all:
g++ $(CXXFLAGS) $(INCLUDES) -o hexgame hexgame.cpp $(LDFLAGS)
mkdir -p ../bin
mv hexgame ../bin
clean:
rm ../bin/hexgame

24
src/build.bat

@ -0,0 +1,24 @@
@echo off
cls
IF "%VisualStudioVersion%" == "" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
SETLOCAL
SET CFLAGS=%CFLAGS% -MT -nologo -Gm- -Oi -WX -W4 -wd4100 -wd4189 -Zi /EHsc
SET INCLUDES=%INCLUDES% /I ..\SDL2\include
SET INCLUDES=%INCLUDES% /I ..\glew\include
SET INCLUDES=%INCLUDES% /I ..\freetype\include
SET _LIBPATH=%_LIBPATH% /LIBPATH:..\SDL2\lib
SET _LIBPATH=%_LIBPATH% /LIBPATH:..\glew\lib
REM SET _LIBPATH=%_LIBPATH% /LIBPATH:..\freetype\lib
REM SET LIBS=%LIBS% SDL2.lib glew32sd.lib opengl32.lib User32.lib libfreetype.dll.a
SET LIBS=%LIBS% SDL2.lib glew32sd.lib opengl32.lib User32.lib
IF NOT EXIST ..\build mkdir ..\build
pushd ..\build
cl %CFLAGS% %INCLUDES% ..\src\hexgame.cpp %LIBS% /link %_LIBPATH%
popd
ENDLOCAL

441
src/gooey.h

@ -0,0 +1,441 @@
#ifndef GOOEY_H
#define GOOEY_H
#include <string>
#include <sstream>
#include <string.h> // for strlen
#include <assert.h>
#include <math.h> // for round
#include <GL/glew.h>
#include <SDL2/SDL_opengl.h>
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#include "hexgame.h"
using std::string;
using std::stringstream;
using std::lround;
struct bitmap_info
{
int32 w;
int32 h;
int32 buffer_size;
uint8 *bitmap_data;
};
struct button
{
bool selected;
uint32 color;
uint32 current_color;
v4i coords; // x0,y0 top left, x1,y1 bottom right
char *text;
HexDrawMode mode;
};
// TODO: heap globals
uint8 *font_filedata;
bitmap_info test_bitmap;
stbtt_aligned_quad *line_buffer;
button *button_array;
uint32 button_count = 0;
uint32 MAX_BUTTONS = 128;
// TODO: stack globals
stbtt_bakedchar font_chardata[96]; // ASCII 32..126 is 95 glyphs
GLuint texture_atlas;
float font_height = 24;
v2i font_atlas_dims = v2i(512, 512);
v4i gooey_extents = {};
// forward declarations
inline void setProjectionMatrix(int w, int h);
void debugLog(string error);
// TODO: move file i/o to asset system
void *
readEntireFile(char *filename)
{
void *buffer = 0;
#if defined _WIN32
stringstream ss;
HANDLE filehandle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (filehandle != INVALID_HANDLE_VALUE)
{
LARGE_INTEGER filesize;
if (GetFileSizeEx(filehandle, &filesize))
{
buffer = VirtualAlloc(0, filesize.QuadPart, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (buffer)
{
assert(filesize.QuadPart <= 0xFFFFFFFF); // don't read files larger than 4gb
uint32 filesize32 = (uint32) filesize.QuadPart;
if (ReadFile(filehandle, buffer, filesize32, NULL, NULL))
{
// success
}
else
{
DWORD win_err = GetLastError();
ss << "error reading file " << filename << " . error code: " << win_err
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
CloseHandle(filehandle);
VirtualFree(buffer, 0, MEM_RELEASE);
}
}
else
{
ss << "error allocating memory for " << filename
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
CloseHandle(filehandle);
}
}
else
{
ss << "failed to get filesize of " << filename
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
CloseHandle(filehandle);
}
}
else
{
ss << "failed to open " << filename
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
}
#else // TODO: other platforms IO
return NULL;
#endif
return buffer;
}
v2f
setTextLine(stbtt_aligned_quad *buffer, char *lineText)
{
// NOTE: not accounting for font metrics at all
v2f lineDims = {};
if (buffer && lineText)
{
float x = 0, y = 0;
int i = 0;
while (*lineText)
{
stbtt_GetBakedQuad(font_chardata, test_bitmap.w, test_bitmap.h,
*lineText - 32, &x, &y, &buffer[i], 1);
stbtt_aligned_quad q = buffer[i];
lineDims.x = q.x1 + font_chardata[i].xadvance;
if (lineDims.y > q.y0)
{
lineDims.y = -1 * q.y0;
}
lineText++;
i++;
}
}
return lineDims;
}
// Xoff, Yoff are offsets in screen coords from bottom left
void
renderTextLine(stbtt_aligned_quad *buffer, int32 numQuads, float Xoff, float Yoff,
v2i screen_dims)
{
int w = screen_dims.x, h = screen_dims.y;
w = (w == 0) ? 1 : w; // don't divide by zero
h = (h == 0) ? 1 : h;
// stb_truetype uses top-down coordinates, so we reverse our projection matrix
// when rendering the text
glMatrixMode(GL_PROJECTION);
real32 a = 2.0f / (real32) w;
real32 b = 2.0f / (real32) h;
real32 matrix[16] = {
a, 0, 0, 0,
0, -b, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1,
};
glLoadMatrixf(matrix);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture_atlas);
glColor4f(0,0,0,1);
glBegin(GL_QUADS);
stbtt_aligned_quad q;
for (int i = 0; i < numQuads; i++)
{
q = buffer[i];
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0 + Xoff,q.y0 - Yoff);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1 + Xoff,q.y0 - Yoff);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1 + Xoff,q.y1 - Yoff);
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0 + Xoff,q.y1 - Yoff);
}
glEnd();
glDisable(GL_TEXTURE_2D);
// reset normal projection matrix
setProjectionMatrix(screen_dims.x, screen_dims.y);
}
void
addButton(char *text, int32 x, int32 y, HexDrawMode mode)
{
if (button_count >= MAX_BUTTONS)
{
// allocate some more memory?
return;
}
v2f lineDims = setTextLine(line_buffer, text);
gooey_extents.x1 = (gooey_extents.x1 >= lineDims.x) ? gooey_extents.x1 : lround(lineDims.x);
gooey_extents.y0 = (gooey_extents.y0 <= y) ? gooey_extents.y0 : y;
v4i coords = { x, y, lround(lineDims.x), y + lround(lineDims.y) };
button b;
b.selected = false;
b.color = b.current_color = 0xCCCCCCFF;
b.coords = coords;
b.text = text;
b.mode = mode;
button_array[button_count] = b;
button_count++;
}
void
createGooeyControls(v2i screenDims)
{
gooey_extents.x0 = 0;
gooey_extents.y1 = screenDims.y;
gooey_extents.y0 = screenDims.y - (int32) font_height;
int32 x = 10;
int32 y = screenDims.y - (int32) font_height - 5;
addButton("Hex Fill", x, y, FILL);
addButton("Draw Line", x, lround(y - font_height), LINE);
addButton("Cone Fill", x, lround(y - 2*font_height), CONE_FILL);
addButton("Pathfinding", x, lround(y - 3*font_height), PATHFINDING);
}
bool
InitGooey(v2i screenDims)
{
bool retVal = false;
stringstream ss;
// TODO: hard coded fontname loaded at runtime
char *fontname = "LiberationMono-Regular.ttf";
font_filedata = (uint8 *) readEntireFile(fontname);
if (font_filedata)
{
test_bitmap.w = font_atlas_dims.x;
test_bitmap.h = font_atlas_dims.y;
test_bitmap.bitmap_data = (uint8 *) VirtualAlloc(0, test_bitmap.w * test_bitmap.h,
MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (test_bitmap.bitmap_data)
{
int bake_result = stbtt_BakeFontBitmap(font_filedata, 0, font_height,
test_bitmap.bitmap_data, test_bitmap.w, test_bitmap.h, 32, 96,
font_chardata
);
if (bake_result !=0)
{
glGenTextures(1, &texture_atlas);
glBindTexture(GL_TEXTURE_2D, texture_atlas);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, test_bitmap.w, test_bitmap.h, 0,
GL_ALPHA, GL_UNSIGNED_BYTE, test_bitmap.bitmap_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// TODO: get screen width / font metrics width for memory allocation
line_buffer = (stbtt_aligned_quad *) VirtualAlloc(0, sizeof(stbtt_aligned_quad)*128, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
// TODO: probably a better way to dynamically allocate memory for an
// amount of 'button' objects we don't know the count of
button_array = (button *) VirtualAlloc(0,
sizeof(button) * MAX_BUTTONS,
MEM_COMMIT|MEM_RESERVE,
PAGE_READWRITE
);
if (line_buffer)
{
retVal = true;
createGooeyControls(screenDims);
}
else
{
ss << "error allocating memory for " << fontname
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
retVal = false;
}
}
else
{
retVal = false;
}
}
else
{
ss << "error allocating memory for " << fontname
<< ". file:" << __FILE__ << ", line: " << __LINE__ - 3;
debugLog(ss.str());
ss.str("");
retVal = false;
}
}
else
{
// file read error
return retVal;
}
return retVal;
}
// TODO: global
struct
{
real32 R = 90.0f/255.0f;
real32 G = 90.0f/255.0f;
real32 B = 90.0f/255.0f;
} G_COLORS;
void
renderGooey(v2i screenDims)
{
real32 x0 = (real32) gooey_extents.x0;
real32 y0 = (real32) gooey_extents.y0;
real32 x1 = (real32) gooey_extents.x1;
real32 y1 = (real32) gooey_extents.y1;
glColor4f(G_COLORS.R, G_COLORS.G, G_COLORS.B, 1);
glBegin(GL_QUADS);
glVertex2f(x0, y0);
glVertex2f(x1, y0);
glVertex2f(x1, y1);
glVertex2f(x0, y1);
glEnd();
for (uint32 i = 0; i < button_count; i++)
{
button b = button_array[i];
// render colored background of button
glColor4ub(
GLubyte((b.current_color & 0xFF000000) >> 24),
GLubyte((b.current_color & 0x00FF0000) >> 16),
GLubyte((b.current_color & 0x0000FF00) >> 8),
GLubyte((b.current_color & 0x000000FF))
);
glBegin(GL_QUADS);
glVertex2i(b.coords.x0, b.coords.y0);
glVertex2i(b.coords.x1, b.coords.y0);
glVertex2i(b.coords.x1, b.coords.y1);
glVertex2i(b.coords.x0, b.coords.y1);
glEnd();
setTextLine(line_buffer, b.text);
renderTextLine(line_buffer, (int32) strlen(b.text), (float) b.coords.x0,
(float) b.coords.y0, screenDims);
}
}
bool
gooeyHitTest(v2i screenCoords, v4i extents = gooey_extents)
{
if (screenCoords.x >= extents.x0 &&
screenCoords.y >= extents.y0 &&
screenCoords.x <= extents.x1 &&
screenCoords.y <= extents.y1)
{
return true;
}
return false;
}
HexDrawMode
gooeyPressButton(v2i screenCoords)
{
// operate as a modal dialog
button *b;
HexDrawMode retVal = NONE;
for (uint32 i = 0; i < button_count; i++)
{
b = &button_array[i];
if (gooeyHitTest(screenCoords, b->coords))
{
b->selected = true;
b->current_color = 0xFFFFFFFF;
retVal = b->mode;
for (uint32 j = 0; j < button_count; j++)
{
if (i != j)
{
b = &button_array[j];
b->selected = false;
b->current_color = b->color;
}
}
break;
}
}
return retVal;
}
void
freeGooey()
{
if (font_filedata)
{
VirtualFree(font_filedata, 0, MEM_RELEASE);
}
if (test_bitmap.bitmap_data)
{
VirtualFree(test_bitmap.bitmap_data, 0, MEM_RELEASE);
}
if (line_buffer)
{
VirtualFree(line_buffer, 0, MEM_RELEASE);
}
if (button_array)
{
VirtualFree(button_array, 0, MEM_RELEASE);
}
}
#endif // GOOEY_H

677
src/hexgame.cpp

@ -0,0 +1,677 @@
/*
* TODO:
*
* - Platform Layer:
* - make a global debug logging function in hexgame.h
* - centralize memory allocation/deallocation
* - custom dynamic arrays in C:
* https://stackoverflow.com/questions/3536153/c-dynamically-growing-array
* - File IO
*
* - Debug UI:
* - font metrics
* - render lines of glyphs to texture
* - simple buttons to select game states
*
* - game states:
* - hex fill, line draw, 'cone' fill, pathfinding, line of sight
*
* - utility functions:
* - SafeRatio(): don't divide by zero
* - SafeTruncate() for various types: check for input size before losing high bits
*/
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <cmath>
#include <random>
#if defined(_WIN32)
#include <windows.h>
#include <Winuser.h>
#endif
#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <SDL2/SDL_opengl.h>
#include "hexgame.h"
#include "hexlib.h"
//#include "gooey.h"
using std::string;
using std::stringstream;
using std::vector;
// TODO: maybe switch to c-style strings for errors? working with stringstream
// is annoying and too verbose
void
debugLog(string error)
{
#if defined(_WIN32) && !defined(__MINGW32__)
error.push_back('\n');
OutputDebugString((LPCSTR) error.c_str());
#else
std::cout << error << '\n';
#endif
}
// TODO: global variables
v2i viewportDims(1280, 720);
vector<hex_info> *hexInfoArray;
Layout global_layout(layout_flat, Point(25, 25), Point(viewportDims.x/2,viewportDims.y/2));
uint32 selected_line_color = 0xFFFFFFFF;
uint32 line_color = 0xFFFFFFFF;
uint32 selected_fill_color = 0xF46000FF;
uint32 fill_color = 0x5C5C5CFF;
struct {
real32 R = 75.0f / 255.0f;
real32 G = 135.0f / 255.0f;
real32 B = 135.0f / 255.0f;
real32 A = 1;
} gl_clear_color;
void
renderHexagon(vector<Point> &vertices, uint32 fillColor, uint32 lineColor)
{
glColor4ub(
uint8((fillColor & 0xFF000000) >> 24),
uint8((fillColor & 0x00FF0000) >> 16),
uint8((fillColor & 0x0000FF00) >> 8),
uint8((fillColor & 0x000000FF))
);
glBegin(GL_POLYGON);
glVertex3d(vertices[0].x, vertices[0].y, 0);
glVertex3d(vertices[1].x, vertices[1].y, 0);
glVertex3d(vertices[2].x, vertices[2].y, 0);
glVertex3d(vertices[3].x, vertices[3].y, 0);
glVertex3d(vertices[4].x, vertices[4].y, 0);
glVertex3d(vertices[5].x, vertices[5].y, 0);
glEnd();
glColor4ub(
uint8((lineColor & 0xFF000000) >> 24),
uint8((lineColor & 0x00FF0000) >> 16),
uint8((lineColor & 0x0000FF00) >> 8),
uint8((lineColor & 0x000000FF))
);
glLineWidth(1.0f);
glBegin(GL_LINE_LOOP);
glVertex3d(vertices[0].x, vertices[0].y, 0);
glVertex3d(vertices[1].x, vertices[1].y, 0);
glVertex3d(vertices[2].x, vertices[2].y, 0);
glVertex3d(vertices[3].x, vertices[3].y, 0);
glVertex3d(vertices[4].x, vertices[4].y, 0);
glVertex3d(vertices[5].x, vertices[5].y, 0);
glEnd();
}
void
setProjectionMatrix(int w, int h)
{
w = (w == 0) ? 1 : w; // don't divide by zero
h = (h == 0) ? 1 : h;
glMatrixMode(GL_PROJECTION);
real32 a = 2.0f / (real32) w;
real32 b = 2.0f / (real32) h;
real32 matrix[16] = {
a, 0, 0, 0,
0, b, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1,
};
glLoadMatrixf(matrix);
// TODO: Make helper function for glGetError(), also map codes to
// error strings
GLenum e = glGetError();
if (e != GL_NO_ERROR)
{
stringstream ss;
ss << "setProjectionMatrix(), GLerror: " << e;
debugLog(ss.str());
}
}
uint32
createRandomColor(uint8 alpha = 205)
{
// http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution
// NOTE: gcc on mingw-w54 produces the same results every time without a
// non-deterministic source
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> uniI(0, 255);
uint32 color = (
(uniI(gen) << 24) // red
| (uniI(gen) << 16) // green
| (uniI(gen) << 8) // blue
| alpha // alpha
);
return color;
}
bool
InitGraphics(SDL_Handles &handles)
{
// TODO: pretty much all of these calls need error handling :(
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
SDL_Window *window = SDL_CreateWindow("hexgame", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
viewportDims.x, viewportDims.y, SDL_WINDOW_OPENGL);
if (window == NULL) {
stringstream e;
e << "Could not create window: " << SDL_GetError();
debugLog(e.str());
return false;
}
handles.window = window;
handles.glContext = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1); // vsync
// enable blending
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// init opengl projection/model matrices
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
setProjectionMatrix(viewportDims.x, viewportDims.y);
// TODO: Check for errors and return false
return true;
}
void
createScene()
{
// create a hexagonal grid of hexagons
int map_radius = 7;
for (int q = -map_radius; q <= map_radius; q++)
{
int r1 = std::max(-map_radius, -q - map_radius);
int r2 = std::min(map_radius, -q + map_radius);
for (int r = r1; r <= r2; r++)
{
hex_info hxi;
hxi.hexID = (int32) hexInfoArray->size();
hxi.hex.q = q; hxi.hex.r = r; hxi.hex.s = -q-r;
Point p = hex_to_pixel(global_layout, hxi.hex);
hxi.XPos = p.x;
hxi.YPos = p.y;
hxi.current_color = fill_color;
hxi.stored_color = hxi.current_color;
hxi.vertices = polygon_corners(global_layout, hxi.hex);
hxi.vertices.shrink_to_fit();
hexInfoArray->push_back(hxi);
}
}
}
// TODO: global variables
bool isSelecting = false;
hex_info *startHex;
hex_info *currentHex;
vector<hex_info> selectedHexes;
HexDrawMode globalDrawMode = NONE;
// TODO: many globals used here
void
debugRender()
{
if (globalDrawMode == CONE_FILL)
{
if (startHex && currentHex)
{
Point p1 = hex_to_pixel(global_layout, startHex->hex);
Point p2 = hex_to_pixel(global_layout, currentHex->hex);
// NOTE: some cone drawing shenanigans
real32 angle = (real32) atan2(p2.y - p1.y, p2.x - p1.x);
real32 len = (real32) hypot(p2.y - p1.y, p2.x - p1.x);
real32 coneAngle = 24.5f * (real32) M_PI / 180;
// TODO: add matrix math library for rotation transform?
// |x| | cos(a + b) -sin(a + b) 0 |
// |y| * | sin(a + b) cos(a + b) 0 |
// |0| | 0 0 1 |
//
real32 x1 = len * cosf(angle);
real32 y1 = len * sinf(angle);
// top of cone
real32 topX = x1 * cosf(coneAngle) - y1 * sinf(coneAngle) + (real32) p1.x;
real32 topY = x1 * sinf(coneAngle) + y1 * cosf(coneAngle) + (real32) p1.y;
// bottom of cone
real32 botX = x1 * cosf(coneAngle) + y1 * sinf(coneAngle) + (real32) p1.x;
real32 botY = x1 * sinf(-1 * coneAngle) + y1 * cosf(coneAngle) + (real32) p1.y;
glColor4f(1.0f, 1.0f, 0.5f, 1.0f);
glLineWidth(1.0f);
glBegin(GL_LINE_LOOP);
glVertex3d(p1.x, p1.y, 0);
glVertex3f(botX, botY, 0);
glVertex3d(p2.x, p2.y, 0);
glVertex3f(topX, topY, 0);
glEnd();
}
}
}
void
render()
{
glClearColor(gl_clear_color.R, gl_clear_color.G, gl_clear_color.B,
gl_clear_color.A);
glClear(GL_COLOR_BUFFER_BIT);
// hexagons
for (hex_info hxi : *hexInfoArray)
{
renderHexagon(hxi.vertices, hxi.current_color,
(hxi.selected) ? selected_line_color : line_color);
}
debugRender();
//renderGooey(viewportDims);
// TODO: Make helper function for glGetError(), also map codes to
// error strings
GLenum e = glGetError();
if (e != GL_NO_ERROR)
{
stringstream ss;
ss << "render(), GLerror: " << e;
debugLog(ss.str());
}
}
v2i
mapMouseToViewport(int32 x, int32 y)
{
v2i coords;
coords.x = x;
coords.y = viewportDims.y - y;
return coords;
}
void
resetHexes()
{
startHex = currentHex = 0;
for (hex_info &hxi : *hexInfoArray)
{
hxi.selected = false;
hxi.current_color = hxi.stored_color;
}
}
hex_info *
getSingleHex(int32 x, int32 y)
{
Point p(x, y);
Hex h = hex_round(pixel_to_hex(global_layout, p));
for (hex_info &hxi : *hexInfoArray)
{
if (hex_equal(h, hxi.hex))
{
return &hxi;
}
}
return 0;
}
void
setStartHex(hex_info *hex)
{
hex->selected = true;
hex->current_color = selected_fill_color;
startHex = currentHex = hex;
isSelecting = true;
}
void
startHexFill(int32 x, int32 y)
{
resetHexes();
hex_info *hex = getSingleHex(x, y);
if (hex)
{
setStartHex(hex);
}
}
void
updateHexFill(int32 x, int32 y)
{
if (isSelecting)
{
hex_info *hxi = getSingleHex(x, y);
if (hxi && (hxi != currentHex))
{
currentHex = hxi;
int l = hex_distance(startHex->hex, currentHex->hex);
for (hex_info &h : *hexInfoArray)
{
if (hex_distance(startHex->hex, h.hex) <= l)
{
h.selected = true;
h.current_color = selected_fill_color;
}
else
{
h.selected = false;
h.current_color = h.stored_color;
}
}
}
}
}
void
startHexLineDraw(int32 x, int32 y)
{
resetHexes();
hex_info *hex = getSingleHex(x, y);
if (hex)
{
setStartHex(hex);
}
}
void
updateHexLineDraw(int32 x, int32 y)
{
if (isSelecting)
{
hex_info *hxi = getSingleHex(x, y);
if (hxi && (hxi != currentHex))
{
currentHex = hxi;
vector<Hex> hexLine = hex_linedraw(startHex->hex, hxi->hex);
for (hex_info &h1 : *hexInfoArray)
{
for (uint i = 0; i < hexLine.size(); i++)
{
Hex h2 = hexLine[i];
if (hex_equal(h1.hex, h2))
{
h1.selected = true;
h1.current_color = selected_fill_color;
break;
}
else if (i == hexLine.size() - 1)
{
h1.selected = false;
h1.current_color = h1.stored_color;
}
}
}
}
}
}
void
startHexConeFill(int32 x, int32 y)
{
resetHexes();
hex_info *hex = getSingleHex(x, y);
if (hex)
{
setStartHex(hex);
}
}
void
updateHexConeFill(int32 x, int32 y)
{
if (isSelecting)
{
hex_info *hxi = getSingleHex(x, y);
if (hxi && (hxi != currentHex))
{
currentHex = hxi;
for (hex_info &h : *hexInfoArray)
{
if (&h == startHex)
{
continue;
}
if (hex_equal(h.hex, hxi->hex))
{
h.selected = true;
h.current_color = selected_fill_color;
}
else
{
h.selected = false;
h.current_color = h.stored_color;
}
}
}
}
}
void
handleMouseDown(SDL_MouseButtonEvent &e)
{
v2i coords = mapMouseToViewport(e.x, e.y);
// mouse click handled by gui
/*
if (gooeyHitTest(coords))
{
HexDrawMode mode = gooeyPressButton(coords);
if (mode != NONE)
{
resetHexes();
globalDrawMode = mode;
}
return;
}
*/
switch (globalDrawMode)
{
case FILL:
startHexFill(coords.x, coords.y);
break;
case LINE:
startHexLineDraw(coords.x, coords.y);
break;
case CONE_FILL:
startHexConeFill(coords.x, coords.y);
break;
case PATHFINDING:
break;
case NONE:
// fall through
default:
{
hex_info *hex = getSingleHex(coords.x, coords.y);
if (hex)
{
hex->selected = !hex->selected;
if (hex->selected)
{
hex->current_color = selected_fill_color;
}
else
{
hex->current_color = hex->stored_color;
}
}
}break;
}
}
void
handleMouseMove(SDL_MouseMotionEvent &e)
{
v2i coords = mapMouseToViewport(e.x, e.y);
switch (globalDrawMode)
{
case FILL:
updateHexFill(coords.x, coords.y);
break;
case LINE:
updateHexLineDraw(coords.x, coords.y);
break;
case CONE_FILL:
updateHexConeFill(coords.x, coords.y);
break;
case PATHFINDING:
break;
case NONE:
// fall through
default:
break;
}
}
void
handleMouseUp(SDL_MouseButtonEvent &e)
{
//v2i coords = mapMouseToViewport(e.x, e.y);
switch (globalDrawMode)
{
case FILL:
isSelecting = false;
break;
case LINE:
isSelecting = false;
break;
case CONE_FILL:
isSelecting = false;
break;
case PATHFINDING:
break;
case NONE:
// fall through
default:
break;
}
}
// TODO: use timer in render loop, and call SDL event handler
// per frame with event pump
bool
enterLoop(SDL_Handles &handles)
{
SDL_Event e;
bool quit = false;
while(!quit)
{
// TODO: remove hack to not peg CPU. replace with an actual frame timer
SDL_Delay(16); // ~60hz
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_KEYDOWN:
if ( e.key.keysym.sym == SDLK_ESCAPE)
quit = true;
break;
case SDL_MOUSEBUTTONDOWN:
isSelecting = true;
handleMouseDown(e.button);
break;
case SDL_MOUSEBUTTONUP:
isSelecting = false;
handleMouseUp(e.button);
break;
case SDL_MOUSEMOTION:
handleMouseMove(e.motion);
break;
default:
break;
}
}
render();
SDL_GL_SwapWindow(handles.window);
}
return true;
}
bool
cleanUp(SDL_Handles &handles)
{
// TODO: make a generic cleanup function that can free other types of SDL objects too
//freeGooey();
for (SDL_Surface *surface : handles.texSurfaces)
SDL_FreeSurface(surface);
SDL_GL_DeleteContext(handles.glContext);
SDL_DestroyWindow(handles.window);
SDL_Quit();
return true;
}
#if defined(_WIN32)
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
#else
int main(int argc, char* argv[])
#endif
{
hexInfoArray = new vector<hex_info>;
SDL_Handles handles;
if (InitGraphics(handles) == false)
{
stringstream ss;
ss << "Unable to initialize graphics, exiting";
debugLog(ss.str());
return 1;
}
/*
if (InitGooey(viewportDims) == false)
{
stringstream ss;
ss << "Unable to initialize gooey, exiting";
debugLog(ss.str());
return 1;
}
*/
createScene();
enterLoop(handles);
cleanUp(handles);
delete hexInfoArray;
return 0;
}

99
src/hexgame.h

@ -0,0 +1,99 @@
#ifndef HEXGAME_H
#define HEXGAME_H
#include <vector>
#include <cassert>
#include <SDL2/SDL.h>
#include "hexlib.h"
typedef float real32;
typedef double real64;
typedef int32_t bool32;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint32_t uint32;
struct v2f
{
v2f(): x(0), y(0) {}
v2f(real64 a, real64 b): x(a), y(b) {}
real64 x;
real64 y;
};
struct v2i
{
v2i(int a, int b): x(a), y(b) {}
v2i() : x(0), y(0) {}
int32 x;
int32 y;
};
struct v4i
{
int32 x0;
int32 y0;
int32 x1;
int32 y1;
};
struct SDL_Handles
{
SDL_Window *window;
SDL_GLContext glContext;
std::vector<SDL_Surface*> texSurfaces;
};
struct hex_info
{
int32 hexID = 0;
Hex hex = {};
real64 XPos = 0;
real64 YPos = 0;
uint32 current_color = 0; // RGBA
uint32 stored_color = 0; // RGBA
bool selected = false;
std::vector<Point> vertices;
};
enum HexDrawMode
{
NONE,
FILL,
LINE,
CONE_FILL,
PATHFINDING
};
struct game_state
{
// TODO:
/*
bool isSelecting = false;
hex_info *startHex;
hex_info *currentHex;
vector<hex_info> selectedHexes;
HexDrawMode globalDrawMode = NONE;
Layout global_layout;
*/
};
real32
SafeRatio(real32 dividend, real32 divisor)
{
if (divisor == 0)
return dividend;
else
return dividend / divisor;
}
int32
SafeTruncateToInt32(int64 val)
{
assert(val <= INT32_MAX && val >= INT32_MIN);
return (int32) val;
}
#endif // HEXGAME_H

270
src/hexlib.h

@ -0,0 +1,270 @@
#ifndef HEXLIB_H
#define HEXLIB_H
// Generated code -- http://www.redblobgames.com/grids/hexagons/
#include <cmath>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <iterator>
using std::abs;
using std::max;
using std::vector;
struct Point
{
const double x;
const double y;
Point(double x_, double y_): x(x_), y(y_) {}
};
struct Hex
{
int q;
int r;
int s;
Hex(int q_, int r_, int s_): q(q_), r(r_), s(s_) {}
Hex(): q(0), r(0), s(0) {}
};
struct FractionalHex
{
const double q;
const double r;
const double s;
FractionalHex(double q_, double r_, double s_): q(q_), r(r_), s(s_) {}
};
struct OffsetCoord
{
const int col;
const int row;
OffsetCoord(int col_, int row_): col(col_), row(row_) {}
};
struct Orientation
{
const double f0;
const double f1;
const double f2;
const double f3;
const double b0;
const double b1;
const double b2;
const double b3;
const double start_angle;
Orientation(double f0_, double f1_, double f2_, double f3_, double b0_, double b1_, double b2_, double b3_, double start_angle_): f0(f0_), f1(f1_), f2(f2_), f3(f3_), b0(b0_), b1(b1_), b2(b2_), b3(b3_), start_angle(start_angle_) {}
};
struct Layout
{
const Orientation orientation;
const Point size;
const Point origin;
Layout(Orientation orientation_, Point size_, Point origin_): orientation(orientation_), size(size_), origin(origin_) {}
};
// Forward declarations
Hex hex_add(Hex a, Hex b)
{
return Hex(a.q + b.q, a.r + b.r, a.s + b.s);
}
Hex hex_subtract(Hex a, Hex b)
{
return Hex(a.q - b.q, a.r - b.r, a.s - b.s);
}
Hex hex_scale(Hex a, int k)
{
return Hex(a.q * k, a.r * k, a.s * k);
}
const vector<Hex> hex_directions = {Hex(1, 0, -1), Hex(1, -1, 0), Hex(0, -1, 1), Hex(-1, 0, 1), Hex(-1, 1, 0), Hex(0, 1, -1)};
Hex hex_direction(int direction)
{
return hex_directions[direction];
}
Hex hex_neighbor(Hex hex, int direction)
{
return hex_add(hex, hex_direction(direction));
}
const vector<Hex> hex_diagonals = {Hex(2, -1, -1), Hex(1, -2, 1), Hex(-1, -1, 2), Hex(-2, 1, 1), Hex(-1, 2, -1), Hex(1, 1, -2)};
Hex hex_diagonal_neighbor(Hex hex, int direction)
{
return hex_add(hex, hex_diagonals[direction]);
}
int hex_length(Hex hex)
{
return int((abs(hex.q) + abs(hex.r) + abs(hex.s)) / 2);
}
int hex_distance(Hex a, Hex b)
{
return hex_length(hex_subtract(a, b));
}
Hex hex_round(FractionalHex h)
{
int q = int(round(h.q));
int r = int(round(h.r));
int s = int(round(h.s));
double q_diff = abs(q - h.q);
double r_diff = abs(r - h.r);
double s_diff = abs(s - h.s);
if (q_diff > r_diff && q_diff > s_diff)
{
q = -r - s;
}
else
if (r_diff > s_diff)
{
r = -q - s;
}
else
{
s = -q - r;
}
return Hex(q, r, s);
}
FractionalHex hex_lerp(FractionalHex a, FractionalHex b, double t)
{
return FractionalHex(a.q * (1 - t) + b.q * t, a.r * (1 - t) + b.r * t, a.s * (1 - t) + b.s * t);
}
vector<Hex> hex_linedraw(Hex a, Hex b)
{
int N = hex_distance(a, b);
FractionalHex a_nudge = FractionalHex(a.q + 0.000001, a.r + 0.000001, a.s - 0.000002);
FractionalHex b_nudge = FractionalHex(b.q + 0.000001, b.r + 0.000001, b.s - 0.000002);
vector<Hex> results = {};
double step = 1.0 / max(N, 1);
for (int i = 0; i <= N; i++)
{
results.push_back(hex_round(hex_lerp(a_nudge, b_nudge, step * i)));
}
return results;
}
const int EVEN = 1;
const int ODD = -1;
OffsetCoord qoffset_from_cube(int offset, Hex h)
{
int col = h.q;
int row = h.r + int((h.q + offset * (h.q & 1)) / 2);
return OffsetCoord(col, row);
}
Hex qoffset_to_cube(int offset, OffsetCoord h)
{
int q = h.col;
int r = h.row - int((h.col + offset * (h.col & 1)) / 2);
int s = -q - r;
return Hex(q, r, s);
}
OffsetCoord roffset_from_cube(int offset, Hex h)
{
int col = h.q + int((h.r + offset * (h.r & 1)) / 2);
int row = h.r;
return OffsetCoord(col, row);
}
Hex roffset_to_cube(int offset, OffsetCoord h)
{
int q = h.col - int((h.row + offset * (h.row & 1)) / 2);
int r = h.row;
int s = -q - r;
return Hex(q, r, s);
}
const Orientation layout_pointy = Orientation(sqrt(3.0), sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0, sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0, 0.5);
const Orientation layout_flat = Orientation(3.0 / 2.0, 0.0, sqrt(3.0) / 2.0, sqrt(3.0), 2.0 / 3.0, 0.0, -1.0 / 3.0, sqrt(3.0) / 3.0, 0.0);
Point hex_to_pixel(Layout layout, Hex h)
{
Orientation M = layout.orientation;
Point size = layout.size;
Point origin = layout.origin;
double x = (M.f0 * h.q + M.f1 * h.r) * size.x;
double y = (M.f2 * h.q + M.f3 * h.r) * size.y;
return Point(x + origin.x, y + origin.y);
}
FractionalHex pixel_to_hex(Layout layout, Point p)
{
Orientation M = layout.orientation;
Point size = layout.size;
Point origin = layout.origin;
Point pt = Point((p.x - origin.x) / size.x, (p.y - origin.y) / size.y);
double q = M.b0 * pt.x + M.b1 * pt.y;
double r = M.b2 * pt.x + M.b3 * pt.y;
return FractionalHex(q, r, -q - r);
}
Point hex_corner_offset(Layout layout, int corner)
{
Orientation M = layout.orientation;
Point size = layout.size;
double angle = 2.0 * M_PI * (M.start_angle - corner) / 6;
return Point(size.x * cos(angle), size.y * sin(angle));
}
vector<Point> polygon_corners(Layout layout, Hex h)
{
vector<Point> corners = {};
Point center = hex_to_pixel(layout, h);
for (int i = 0; i < 6; i++)
{
Point offset = hex_corner_offset(layout, i);
corners.push_back(Point(center.x + offset.x, center.y + offset.y));
}
return corners;
}
// custom hex functions
bool hex_equal(Hex a, Hex b)
{
return (a.q == b.q && a.r == b.r && a.s == b.s);
}
#endif // HEXLIB_H

4061
src/stb_truetype.h

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save