11 changed files with 1313 additions and 1296 deletions
@ -0,0 +1,100 @@ |
|||||||
|
|
||||||
|
#if defined (_WIN32) |
||||||
|
#include <SDL.h> |
||||||
|
#else |
||||||
|
#include <SDL2/SDL.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "imgui.h" |
||||||
|
#include "examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h" |
||||||
|
|
||||||
|
#include "gooey.h" |
||||||
|
|
||||||
|
bool |
||||||
|
initGooey(SDL_Handles &handles, v2i vp_dims /*TODO: pass in game state*/) |
||||||
|
{ |
||||||
|
IMGUI_CHECKVERSION(); |
||||||
|
ImGui::CreateContext(); |
||||||
|
ImGuiIO& io = ImGui::GetIO(); |
||||||
|
io.IniFilename = NULL; // don't save window state to imgui.ini
|
||||||
|
ImGui_ImplSdlGL3_Init(handles.window); |
||||||
|
io.DisplaySize.x = vp_dims.x; |
||||||
|
io.DisplaySize.y = vp_dims.y; |
||||||
|
ImGui::StyleColorsDark(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
shutdownGooey() |
||||||
|
{ |
||||||
|
ImGui_ImplSdlGL3_Shutdown(); |
||||||
|
ImGui::DestroyContext(); |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
gooeyProcessEvent(SDL_Event &event) |
||||||
|
{ |
||||||
|
ImGui_ImplSdlGL3_ProcessEvent(&event); |
||||||
|
return ImGui::GetIO().WantCaptureMouse; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
renderGooey(SDL_Handles &handles, HexDrawMode &mode, bool &is_debug, |
||||||
|
hex_info* start_hex, hex_info* current_hex, bool &is_selecting) |
||||||
|
{ |
||||||
|
ImGui_ImplSdlGL3_NewFrame(handles.window); |
||||||
|
|
||||||
|
ImGuiWindowFlags window_flags = 0; |
||||||
|
window_flags |= ImGuiWindowFlags_NoTitleBar; |
||||||
|
window_flags |= ImGuiWindowFlags_NoScrollbar; |
||||||
|
window_flags |= ImGuiWindowFlags_NoMove; |
||||||
|
window_flags |= ImGuiWindowFlags_NoResize; |
||||||
|
window_flags |= ImGuiWindowFlags_NoCollapse; |
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0,0)); |
||||||
|
ImGui::SetNextWindowSize(ImVec2(300, 720)); |
||||||
|
ImGui::SetNextWindowBgAlpha(0.3f); |
||||||
|
bool show_window; |
||||||
|
ImGui::Begin("", &show_window, window_flags); |
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO(); |
||||||
|
ImGui::Text("%.3f ms/frame, (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); |
||||||
|
|
||||||
|
ImGui::Text("Draw Mode:"); |
||||||
|
if (ImGui::RadioButton("None", (mode == NONE))) |
||||||
|
mode = NONE; |
||||||
|
if (ImGui::RadioButton("Fill", (mode == FILL))) |
||||||
|
mode = FILL; |
||||||
|
if (ImGui::RadioButton("Line", (mode == LINE))) |
||||||
|
mode = LINE; |
||||||
|
if (ImGui::RadioButton("Cone Fill", (mode == CONE_FILL))) |
||||||
|
mode = CONE_FILL; |
||||||
|
|
||||||
|
ImGui::Checkbox("Debug Render", &is_debug); |
||||||
|
ImGui::SameLine(); ImGui::TextUnformatted(is_debug ? "true" : "false"); |
||||||
|
ImGui::Text("is_selecting"); |
||||||
|
ImGui::SameLine(); ImGui::TextUnformatted(is_selecting ? "true" : "false"); |
||||||
|
|
||||||
|
// testing SDL_image
|
||||||
|
SDL_Surface* image = handles.texSurfaces[0]; |
||||||
|
ImGui::Image((void*)(intptr_t) image->userdata, ImVec2(image->w, image->h)); |
||||||
|
//ImGui::ShowMetricsWindow();
|
||||||
|
|
||||||
|
if (current_hex) |
||||||
|
{ |
||||||
|
Hex ch = current_hex->hex; |
||||||
|
ImGui::Text("current_hex: %i, %i, %i", ch.q, ch.r, ch.s); |
||||||
|
} |
||||||
|
if (start_hex) |
||||||
|
{ |
||||||
|
Hex sh = start_hex->hex; |
||||||
|
ImGui::Text("start_hex: %i, %i, %i", sh.q, sh.r, sh.s); |
||||||
|
} |
||||||
|
|
||||||
|
ImGui::End(); |
||||||
|
ImGui::Render(); |
||||||
|
ImGui_ImplSdlGL3_RenderDrawData(ImGui::GetDrawData()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,243 @@ |
|||||||
|
|
||||||
|
#include "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 OffsetCoord |
||||||
|
{ |
||||||
|
const int col; |
||||||
|
const int row; |
||||||
|
OffsetCoord(int col_, int row_): col(col_), row(row_) {} |
||||||
|
}; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
vector<Hex> |
||||||
|
hex_conefill(Hex a, Hex b) |
||||||
|
{ |
||||||
|
vector<Hex> results = {}; |
||||||
|
|
||||||
|
return results; |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: implementation of this line crossing test
|
||||||
|
// https://graphics.stanford.edu/pub/Graphics/RTNews/html/rtnv5n3.html#art3
|
||||||
|
// NOTE: assumes use of convex polygons with vertices in CCW layout
|
||||||
|
bool |
||||||
|
crossingTest(vector<Point> vertices, Point p) |
||||||
|
{ |
||||||
|
int numVertices = vertices.size(), intersect_count = 0; |
||||||
|
Point vert0, vert1; |
||||||
|
double m, x; |
||||||
|
|
||||||
|
if (numVertices < 3) |
||||||
|
return false; |
||||||
|
|
||||||
|
vert0 = vertices[0]; |
||||||
|
|
||||||
|
for (int i = 1; i < numVertices + 1; i++) |
||||||
|
{ |
||||||
|
// use first vertex for the last edge
|
||||||
|
if (i == numVertices) |
||||||
|
vert1 = vertices[0]; |
||||||
|
else |
||||||
|
vert1 = vertices[i]; |
||||||
|
|
||||||
|
// check if edge can intersect the +X ray
|
||||||
|
// NOTE: upward/downward crossing excludes one vertex in the case that
|
||||||
|
// the ray intersects a vertex
|
||||||
|
if ((vert0.y > p.y && vert1.y <= p.y) || // downward crossing
|
||||||
|
(vert0.y <= p.y && vert1.y > p.y)) // upward crossing
|
||||||
|
{ |
||||||
|
m = (vert1.y - vert0.y) / (vert1.x - vert0.x); |
||||||
|
x = (p.y - vert0.y) / m + vert0.x; |
||||||
|
// if x is right of p.x it must intersect
|
||||||
|
if (x >= p.x) |
||||||
|
intersect_count++; |
||||||
|
|
||||||
|
// 2 intersections of convex polygon means we started outside
|
||||||
|
if (intersect_count > 1) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// start with previous vertex on next loop
|
||||||
|
vert0 = vert1; |
||||||
|
} |
||||||
|
|
||||||
|
return (intersect_count == 1); |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,73 @@ |
|||||||
|
#include <cassert> |
||||||
|
|
||||||
|
#include <glm/glm.hpp> // vec3 |
||||||
|
|
||||||
|
#include "aixlog.hpp" |
||||||
|
|
||||||
|
#include "mesh.h" |
||||||
|
|
||||||
|
|
||||||
|
/* the global Assimp scene object */ |
||||||
|
const aiScene* g_scene = NULL; |
||||||
|
|
||||||
|
bool |
||||||
|
initAssimp() |
||||||
|
{ |
||||||
|
/* get a handle to the predefined STDOUT log stream and attach
|
||||||
|
* it to the logging system. It remains active for all further |
||||||
|
* calls to aiImportFile(Ex) and aiApplyPostProcessing. */ |
||||||
|
aiLogStream stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL); |
||||||
|
aiAttachLogStream(&stream); |
||||||
|
|
||||||
|
g_scene = aiImportFile("../data/animated.block.dae", aiProcessPreset_TargetRealtime_MaxQuality); |
||||||
|
|
||||||
|
if (g_scene->mNumMeshes != 1) { |
||||||
|
LOG(ERROR) << "We Can only handle 1 mesh per entity atm\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
uint |
||||||
|
getVertexCount() |
||||||
|
{ |
||||||
|
assert(g_scene); |
||||||
|
return g_scene->mMeshes[0]->mNumVertices; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: probably don't really need to copy this here
|
||||||
|
// let assimp manage the storage, and just reference indexes after passing
|
||||||
|
// to opengl
|
||||||
|
|
||||||
|
// copy data from assimp for use in our renderer
|
||||||
|
Entity* |
||||||
|
convertMesh(Entity* e) |
||||||
|
{ |
||||||
|
assert(g_scene); |
||||||
|
uint numVertices = getVertexCount(); |
||||||
|
assert(e && e->num_vertices == numVertices); |
||||||
|
|
||||||
|
// copy vertices
|
||||||
|
for (uint i = 0; i < numVertices; i++) { |
||||||
|
aiVector3D v_in = g_scene->mMeshes[0]->mVertices[i]; |
||||||
|
glm::vec3 &v_out = e->vertices[i]; |
||||||
|
v_out.x = v_in.x; |
||||||
|
v_out.y = v_in.y; |
||||||
|
v_out.z = v_in.z; |
||||||
|
} |
||||||
|
|
||||||
|
return e; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
shutdownAssimp() |
||||||
|
{ |
||||||
|
|
||||||
|
/* cleanup - calling 'aiReleaseImport' is important, as the library
|
||||||
|
keeps internal resources until the scene is freed again. Not |
||||||
|
doing so can cause severe resource leaking. */ |
||||||
|
aiReleaseImport(g_scene); |
||||||
|
aiDetachAllLogStreams(); |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,823 @@ |
|||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <cmath> // trig functions |
||||||
|
#include <cstdlib> // calloc |
||||||
|
|
||||||
|
// TODO: decide on extension library
|
||||||
|
//#include <GL/glew.h>
|
||||||
|
#include <GL/gl3w.h> |
||||||
|
|
||||||
|
#if defined (_WIN32) |
||||||
|
#include <SDL.h> |
||||||
|
#else |
||||||
|
#include <SDL2/SDL.h> |
||||||
|
#endif |
||||||
|
#include <SDL_image.h> |
||||||
|
#include <glm/glm.hpp> |
||||||
|
#include <glm/geometric.hpp> |
||||||
|
#include <glm/gtc/matrix_transform.hpp> |
||||||
|
|
||||||
|
#include "aixlog.hpp" |
||||||
|
|
||||||
|
#include "hexlib.h" |
||||||
|
#include "hexgame.h" |
||||||
|
|
||||||
|
#define MOVE_SPEED 5.f |
||||||
|
#define ROTATE_SPEED 0.005f |
||||||
|
#define CAMERA_Z_CLAMP_ANGLE 85.f |
||||||
|
//#define PROJ_TYPE ORTHOGRAPHIC
|
||||||
|
#define PROJ_TYPE PERSPECTIVE |
||||||
|
|
||||||
|
const char * VERTEX_SHADER_CODE = |
||||||
|
"#version 330 core\n" |
||||||
|
"in vec3 vertexPosition_modelspace;\n" |
||||||
|
"in vec3 vertexColor;\n" |
||||||
|
"out vec3 fragmentColor;\n" |
||||||
|
"uniform mat4 model;\n" |
||||||
|
"uniform mat4 view;\n" |
||||||
|
"uniform mat4 projection;\n" |
||||||
|
"void main()\n" |
||||||
|
"{\n" |
||||||
|
" gl_Position = projection * view * model * vec4(vertexPosition_modelspace, 1);\n" |
||||||
|
" fragmentColor = vertexColor;\n" |
||||||
|
"}"; |
||||||
|
|
||||||
|
const char * FRAGMENT_SHADER_CODE = |
||||||
|
"#version 330 core\n" |
||||||
|
"in vec3 fragmentColor;\n" |
||||||
|
"out vec3 color;\n" |
||||||
|
"void main()\n" |
||||||
|
"{\n" |
||||||
|
" color = fragmentColor;\n" |
||||||
|
"}"; |
||||||
|
|
||||||
|
const char * LINE_FRAGMENT_SHADER_CODE = |
||||||
|
"#version 330 core\n" |
||||||
|
"out vec3 color;\n" |
||||||
|
"void main()\n" |
||||||
|
"{\n" |
||||||
|
" color = vec3(0,0,0);\n" |
||||||
|
"}"; |
||||||
|
|
||||||
|
const char * DEBUG_FRAGMENT_SHADER_CODE = |
||||||
|
"#version 330 core\n" |
||||||
|
"out vec3 color;\n" |
||||||
|
"void main()\n" |
||||||
|
"{\n" |
||||||
|
" color = vec3(1,0,0);\n" |
||||||
|
"}"; |
||||||
|
|
||||||
|
typedef struct clear_col |
||||||
|
{ |
||||||
|
real32 R; |
||||||
|
real32 G; |
||||||
|
real32 B; |
||||||
|
real32 A; |
||||||
|
} clear_col; |
||||||
|
clear_col g_clear_col { 75.f / 255.f, 135.f / 255.f, 135.f / 255.f, 1.f }; |
||||||
|
|
||||||
|
typedef struct gl_matrix_info |
||||||
|
{ |
||||||
|
glm::mat4 projection; |
||||||
|
glm::mat4 view; |
||||||
|
glm::mat4 model; |
||||||
|
glm::mat4 MVP; |
||||||
|
} gl_matrix_info; |
||||||
|
|
||||||
|
enum projection_type |
||||||
|
{ |
||||||
|
PERSPECTIVE, |
||||||
|
ORTHOGRAPHIC, |
||||||
|
}; |
||||||
|
|
||||||
|
struct camera |
||||||
|
{ |
||||||
|
glm::vec3 position; |
||||||
|
float hAngle; |
||||||
|
float vAngle; |
||||||
|
glm::vec3 target; |
||||||
|
glm::vec3 forward; |
||||||
|
glm::vec3 up; |
||||||
|
glm::vec3 left; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct gl_buffer |
||||||
|
{ |
||||||
|
GLuint buffer_id = 0; |
||||||
|
size_t buffer_len = 0; // NOTE: number of elements in buffer
|
||||||
|
GLfloat* buffer = nullptr; |
||||||
|
} gl_buffer; |
||||||
|
|
||||||
|
typedef struct gl_render_group |
||||||
|
{ |
||||||
|
// NOTE: For now the renderFrame function will assume these are the same size
|
||||||
|
gl_buffer vertex_buffer; |
||||||
|
gl_buffer color_buffer; |
||||||
|
GLuint program_id = 0; |
||||||
|
GLuint model_matrix_id = 0; |
||||||
|
GLuint view_matrix_id = 0; |
||||||
|
GLuint projection_matrix_id = 0; |
||||||
|
GLuint vertex_array_id = 0; |
||||||
|
Entity* entity = nullptr; |
||||||
|
} gl_render_group; |
||||||
|
|
||||||
|
gl_matrix_info g_scene_matrices; |
||||||
|
gl_render_group g_filled_hex_render_group; |
||||||
|
gl_render_group g_hex_line_render_group; |
||||||
|
gl_render_group g_debug_render_group; |
||||||
|
gl_render_group g_entity_render_group; |
||||||
|
camera g_camera; |
||||||
|
|
||||||
|
|
||||||
|
void |
||||||
|
openglDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, |
||||||
|
GLsizei length, const GLchar* message, const void* userParam) |
||||||
|
{ |
||||||
|
LOG((type == GL_DEBUG_TYPE_ERROR) ? ERROR : DEBUG) |
||||||
|
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "") |
||||||
|
<< ", type: " << type |
||||||
|
<< ", severity: " << severity |
||||||
|
<< ", message: " << message << "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
addTexture(SDL_Handles &handles, const char * path) |
||||||
|
{ |
||||||
|
// testing
|
||||||
|
LOG(INFO) << "Loading image: " << path << "\n"; |
||||||
|
SDL_Surface* image = IMG_Load(path); |
||||||
|
|
||||||
|
if (!image) |
||||||
|
{ |
||||||
|
LOG(ERROR) << "IMG_Load: " << IMG_GetError() << "\n"; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
GLuint tex_id; |
||||||
|
glGenTextures(1, &tex_id); |
||||||
|
glBindTexture(GL_TEXTURE_2D, tex_id); |
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||||||
|
|
||||||
|
// store opengl id in SDL_Surface.userdat
|
||||||
|
image->userdata = (void*)(intptr_t) tex_id; |
||||||
|
handles.texSurfaces.push_back(image); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
v2f |
||||||
|
getUnprojectedCoords(int32 x, int32 y, int32 vp_width, int32 vp_height) |
||||||
|
{ |
||||||
|
// NOTE: using depth buffer may not be as accurate as doing ray-cast
|
||||||
|
GLfloat depth; |
||||||
|
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); |
||||||
|
glm::vec4 viewport = glm::vec4(0, 0, vp_width, vp_height); |
||||||
|
glm::vec3 wincoord = glm::vec3(x, y, depth); |
||||||
|
glm::vec3 vU = glm::unProject(wincoord, g_scene_matrices.view, g_scene_matrices.projection, viewport); |
||||||
|
v2f v(vU.x, vU.y); |
||||||
|
|
||||||
|
return v; |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: model_name, view_name, and projection_name should match the matrix variable
|
||||||
|
// names in the shader source
|
||||||
|
bool |
||||||
|
initShaderProgram(gl_render_group &rg, const char * vertex_code, const char * frag_code, |
||||||
|
const char* model_name, const char* view_name, const char* projection_name) |
||||||
|
{ |
||||||
|
glGenVertexArrays(1, &rg.vertex_array_id); |
||||||
|
glBindVertexArray(rg.vertex_array_id); |
||||||
|
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); |
||||||
|
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); |
||||||
|
|
||||||
|
glShaderSource(vertex_shader_id, 1, &vertex_code, NULL); |
||||||
|
glShaderSource(fragment_shader_id, 1, &frag_code, NULL); |
||||||
|
glCompileShader(vertex_shader_id); |
||||||
|
glCompileShader(fragment_shader_id); |
||||||
|
|
||||||
|
rg.program_id = glCreateProgram(); |
||||||
|
glAttachShader(rg.program_id, vertex_shader_id); |
||||||
|
glAttachShader(rg.program_id, fragment_shader_id); |
||||||
|
glLinkProgram(rg.program_id); |
||||||
|
|
||||||
|
rg.model_matrix_id = glGetUniformLocation(rg.program_id, model_name); |
||||||
|
rg.view_matrix_id = glGetUniformLocation(rg.program_id, view_name); |
||||||
|
rg.projection_matrix_id = glGetUniformLocation(rg.program_id, projection_name); |
||||||
|
|
||||||
|
glDetachShader(rg.program_id, vertex_shader_id); |
||||||
|
glDetachShader(rg.program_id, fragment_shader_id); |
||||||
|
glDeleteShader(vertex_shader_id); |
||||||
|
glDeleteShader(fragment_shader_id); |
||||||
|
|
||||||
|
// check for errors
|
||||||
|
GLint isLinked = 0; |
||||||
|
glGetProgramiv(rg.program_id, GL_LINK_STATUS, &isLinked); |
||||||
|
if (isLinked == GL_FALSE) { |
||||||
|
GLint maxLength = 0; |
||||||
|
glGetProgramiv(rg.program_id, GL_INFO_LOG_LENGTH, &maxLength); |
||||||
|
|
||||||
|
// The maxLength includes the NULL character
|
||||||
|
GLchar infoLog[maxLength]; |
||||||
|
glGetProgramInfoLog(rg.program_id, maxLength, &maxLength, &infoLog[0]); |
||||||
|
LOG(ERROR) << infoLog << "\n"; |
||||||
|
|
||||||
|
// The program is useless now. So delete it.
|
||||||
|
glDeleteProgram(rg.program_id); |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
initMatrices(projection_type p) |
||||||
|
{ |
||||||
|
// TODO: many constants used here should be passed as args
|
||||||
|
|
||||||
|
if (p == PERSPECTIVE) |
||||||
|
{ |
||||||
|
g_scene_matrices.projection = glm::infinitePerspective( |
||||||
|
glm::radians(60.f), // FoV
|
||||||
|
16.f / 9.f, // ascpect ratio
|
||||||
|
0.1f // near clip plane
|
||||||
|
); |
||||||
|
|
||||||
|
g_camera.position = glm::vec3(640,0,100);
|
||||||
|
g_camera.target = glm::vec3(640,500,0); |
||||||
|
// inital rotation should match target direction
|
||||||
|
glm::vec3 &p = g_camera.position; |
||||||
|
glm::vec3 &t = g_camera.target; |
||||||
|
g_camera.hAngle = 0; |
||||||
|
g_camera.vAngle = glm::atan((t.z - p.z) / (t.y - p.y)); |
||||||
|
|
||||||
|
//////
|
||||||
|
// TODO: add call to rotate camera here to remove duplicate code
|
||||||
|
camera &c = g_camera; |
||||||
|
float &h = c.hAngle; |
||||||
|
float &v = c.vAngle; |
||||||
|
c.forward = glm::vec3( |
||||||
|
glm::cos(v) * glm::sin(h), |
||||||
|
glm::cos(v) * glm::cos(h), |
||||||
|
glm::sin(v) |
||||||
|
); |
||||||
|
//////
|
||||||
|
|
||||||
|
g_camera.up = glm::vec3(0,1,0); |
||||||
|
g_camera.left = glm::normalize(glm::cross(g_camera.up, g_camera.forward)); |
||||||
|
g_camera.up = glm::cross(g_camera.forward, g_camera.left); |
||||||
|
|
||||||
|
g_scene_matrices.view = glm::lookAt( |
||||||
|
g_camera.position, // camera position
|
||||||
|
g_camera.position + g_camera.forward, |
||||||
|
g_camera.up // "up" vector
|
||||||
|
); |
||||||
|
} |
||||||
|
else // ORTHO
|
||||||
|
{ |
||||||
|
// left, right, bottom, top, zNear, zFar
|
||||||
|
g_scene_matrices.projection = glm::ortho(0.f, 1280.0f, 0.f, 720.0f, 0.1f, 100.0f); |
||||||
|
g_scene_matrices.view = glm::lookAt( |
||||||
|
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
|
||||||
|
glm::vec3(0.0f, 0.0f, 0.0f), // look at position
|
||||||
|
glm::vec3(0,1,0) // "up" vector
|
||||||
|
); |
||||||
|
}
|
||||||
|
|
||||||
|
g_scene_matrices.model = glm::mat4(1.0f); |
||||||
|
g_scene_matrices.MVP = g_scene_matrices.projection * g_scene_matrices.view * g_scene_matrices.model;
|
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: this will only work after a call to glBindBuffer
|
||||||
|
bool |
||||||
|
checkGLBufferSize(GLenum buf_type, int expected_size, int line_num) |
||||||
|
{ |
||||||
|
GLint gl_size; |
||||||
|
glGetBufferParameteriv(buf_type, GL_BUFFER_SIZE ,&gl_size); |
||||||
|
|
||||||
|
if (expected_size != gl_size) |
||||||
|
{ |
||||||
|
LOG(ERROR) << "line: " << line_num << "gl buffer size mismatch\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
initGLBufferObject(gl_buffer* buf_obj, int len, GLenum usage, GLfloat data[]) |
||||||
|
{ |
||||||
|
buf_obj->buffer_len = (size_t) len; |
||||||
|
buf_obj->buffer = (GLfloat*) std::calloc(len, sizeof(GLfloat)); |
||||||
|
|
||||||
|
if (data) |
||||||
|
{ |
||||||
|
for (int i = 0; i < len; i++) |
||||||
|
buf_obj->buffer[i] = data[i]; |
||||||
|
} |
||||||
|
|
||||||
|
glGenBuffers(1, &buf_obj->buffer_id); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, buf_obj->buffer_id); |
||||||
|
glBufferData(GL_ARRAY_BUFFER, len * sizeof(GLfloat), buf_obj->buffer, usage); |
||||||
|
|
||||||
|
if (!checkGLBufferSize(GL_ARRAY_BUFFER, len * sizeof(GLfloat), __LINE__)) |
||||||
|
return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
initRenderer(SDL_Handles &handles, v2i vpDims) |
||||||
|
{ |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
||||||
|
// TODO: Line drawing quality seems to depend on graphics driver whims
|
||||||
|
// maybe try using textured billboards instead?
|
||||||
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||||
|
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||||
|
SDL_GL_SetSwapInterval(1); // vsync
|
||||||
|
SDL_GetCurrentDisplayMode(0, &handles.currentDisplayMode); |
||||||
|
handles.window =
|
||||||
|
SDL_CreateWindow("hexgame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, |
||||||
|
vpDims.x, vpDims.y, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); |
||||||
|
|
||||||
|
if (handles.window == NULL) |
||||||
|
{ |
||||||
|
// TODO: log error SDL_GetError()
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
handles.glContext = SDL_GL_CreateContext(handles.window); |
||||||
|
|
||||||
|
// TODO: decide on extension library
|
||||||
|
gl3wInit(); |
||||||
|
#if 0 |
||||||
|
// Initialize GLEW
|
||||||
|
// glewExperimental is only needed in GLEW <= 1.13.0
|
||||||
|
// we can require version 2.0.0+
|
||||||
|
//glewExperimental = true; // Needed for core profile
|
||||||
|
GLenum err = glewInit(); |
||||||
|
if (err != GLEW_OK) { |
||||||
|
LOG(ERROR) << "Failed to initilize GLEW" << glewGetErrorString(err) << "\n"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
LOG(INFO) << "opengl vendor: " << glGetString(GL_VENDOR) << "\n"; |
||||||
|
LOG(INFO)<< "opengl renderer: " << glGetString(GL_RENDERER) << "\n"; |
||||||
|
LOG(INFO) << "opengl version: " << glGetString(GL_VERSION) << "\n"; |
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST); |
||||||
|
glEnable(GL_LINE_SMOOTH); |
||||||
|
|
||||||
|
// TODO: blending, these options break the http://www.opengl-tutorial.org tutorials atm
|
||||||
|
// Setup render state: alpha-blending enabled, polygon fill
|
||||||
|
#if 1 |
||||||
|
glEnable(GL_BLEND); |
||||||
|
glBlendEquation(GL_FUNC_ADD); |
||||||
|
glBlendFunc(GL_ONE, GL_SRC_ALPHA); |
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
||||||
|
#endif |
||||||
|
|
||||||
|
// TODO: glDebugMessageCallback is only availabe from >v4.3
|
||||||
|
// check and warn if context doesn't support this function here
|
||||||
|
glEnable (GL_DEBUG_OUTPUT); |
||||||
|
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0); |
||||||
|
// hide VRAM debug messages
|
||||||
|
glDebugMessageControl(GL_DONT_CARE, 33361, GL_DONT_CARE, 0, 0, GL_FALSE); |
||||||
|
|
||||||
|
if (!initShaderProgram( |
||||||
|
g_filled_hex_render_group, VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE, |
||||||
|
"model", "view", "projection") |
||||||
|
|| !initShaderProgram( |
||||||
|
g_hex_line_render_group, VERTEX_SHADER_CODE, LINE_FRAGMENT_SHADER_CODE, |
||||||
|
"model", "view", "projection") |
||||||
|
|| !initShaderProgram( |
||||||
|
g_debug_render_group, VERTEX_SHADER_CODE, DEBUG_FRAGMENT_SHADER_CODE, |
||||||
|
"model", "view", "projection") |
||||||
|
|| !initShaderProgram( |
||||||
|
g_entity_render_group, VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE, |
||||||
|
"model", "view", "projection")) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
fillTriangleBufferFromHex(GLfloat buf[], int idx, const hex_info &hex) |
||||||
|
{ |
||||||
|
// triangles
|
||||||
|
for (int i = 0; i < 6; i++) |
||||||
|
{ |
||||||
|
// vertex 0
|
||||||
|
buf[idx + 0] = (GLfloat) hex.XPos; |
||||||
|
buf[idx + 1] = (GLfloat) hex.YPos; |
||||||
|
buf[idx + 2] = (GLfloat) 0.f; |
||||||
|
|
||||||
|
// vertex 1
|
||||||
|
buf[idx + 3] = (GLfloat) hex.vertices[i].x; |
||||||
|
buf[idx + 4] = (GLfloat) hex.vertices[i].y; |
||||||
|
buf[idx + 5] = (GLfloat) 0.f; |
||||||
|
|
||||||
|
if (i == 5) // re-use the first point for the last triangle
|
||||||
|
{ |
||||||
|
// vertex 2
|
||||||
|
buf[idx + 6] = (GLfloat) hex.vertices[0].x; |
||||||
|
buf[idx + 7] = (GLfloat) hex.vertices[0].y; |
||||||
|
buf[idx + 8] = (GLfloat) 0.f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// vertex 2
|
||||||
|
buf[idx + 6] = (GLfloat) hex.vertices[i + 1].x; |
||||||
|
buf[idx + 7] = (GLfloat) hex.vertices[i + 1].y; |
||||||
|
buf[idx + 8] = (GLfloat) 0.f; |
||||||
|
} |
||||||
|
|
||||||
|
// we've added 9 GLfloats per loop
|
||||||
|
idx += 9; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: helper for fillColorBuffer() to convert uint32 color to GLfloat triplet
|
||||||
|
void |
||||||
|
convertColor(GLfloat buf[3], uint32 color) |
||||||
|
{ |
||||||
|
// NOTE: not using the alpha values for now
|
||||||
|
buf[0] = (GLfloat) ((color >> 24) & 0xFF) / (GLfloat) 255;
|
||||||
|
buf[1] = (GLfloat) ((color >> 16) & 0xFF) / (GLfloat) 255;
|
||||||
|
buf[2] = (GLfloat) ((color >> 8) & 0xFF) / (GLfloat) 255;
|
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
fillColorBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes) |
||||||
|
{ |
||||||
|
int buf_idx; |
||||||
|
int buf_len_per_hex = 54; // NOTE: 3 * 3 * 6
|
||||||
|
GLfloat color_buf[3]; |
||||||
|
for (int i = 0; i < (int) hexes->size(); i++) |
||||||
|
{ |
||||||
|
buf_idx = i * buf_len_per_hex; |
||||||
|
hex_info hxi = (*hexes)[i]; |
||||||
|
convertColor(color_buf, hxi.current_color); |
||||||
|
|
||||||
|
for (int j = 0; j < buf_len_per_hex; j+=3) |
||||||
|
{ |
||||||
|
buf[buf_idx + j] = color_buf[0]; |
||||||
|
buf[buf_idx + j + 1] = color_buf[1]; |
||||||
|
buf[buf_idx + j + 2] = color_buf[2]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
fillHexLineBuffer(GLfloat buf[], int len, std::vector<hex_info>* hexes) |
||||||
|
{ |
||||||
|
Point p1, p2; |
||||||
|
int idx = 0; |
||||||
|
for (int i = 0; i < (int) hexes->size(); i++) |
||||||
|
{ |
||||||
|
hex_info hxi = (*hexes)[i]; |
||||||
|
for (int j = 0; j < 6; j ++) |
||||||
|
{ |
||||||
|
if (j == 5) // wrap
|
||||||
|
{ |
||||||
|
p1 = hxi.vertices[j]; |
||||||
|
p2 = hxi.vertices[0]; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
p1 = hxi.vertices[j]; |
||||||
|
p2 = hxi.vertices[j + 1]; |
||||||
|
} |
||||||
|
|
||||||
|
buf[idx + 0] = p1.x; |
||||||
|
buf[idx + 1] = p1.y; |
||||||
|
buf[idx + 2] = 0.f; |
||||||
|
buf[idx + 3] = p2.x; |
||||||
|
buf[idx + 4] = p2.y; |
||||||
|
buf[idx + 5] = 0.f; |
||||||
|
idx += 6; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool |
||||||
|
createScene(std::vector<hex_info>* hexes, Entity* entities, uint32 entity_count) |
||||||
|
{ |
||||||
|
initMatrices(PROJ_TYPE); |
||||||
|
|
||||||
|
// Vertex Data
|
||||||
|
|
||||||
|
// TODO: index duplicate vertices
|
||||||
|
// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/
|
||||||
|
int hex_count = (int) hexes->size(); |
||||||
|
// 6 triangles * 3 vertices per triangle * 3 floats per vertex = 54
|
||||||
|
int vbuf_len = hex_count * 6 * 3 * 3; |
||||||
|
|
||||||
|
// TODO: surely there's a way to use indexed drawing for line vertices eg) line_loop
|
||||||
|
// and still use one buffer for multiple shapes/paths gldrawarraysintanced?, gldrawelements?
|
||||||
|
// https://gamedev.stackexchange.com/questions/104310/opengl-4-5-primitive-restart-vs-base-index
|
||||||
|
int line_vertices_per_hex = 6 * 2; // 12 vertices since we're using line segments atm
|
||||||
|
//gl_buffer *line_vertex_buffer = &g_hex_line_render_group.vertex_buffer;
|
||||||
|
int line_buf_len = hexes->size() * line_vertices_per_hex * 3; // 3 floats per vertex
|
||||||
|
|
||||||
|
// temporary buffers
|
||||||
|
GLfloat* vbuf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
||||||
|
GLfloat* cbuf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
||||||
|
GLfloat* line_buf = (GLfloat*) std::calloc(vbuf_len, sizeof(GLfloat)); |
||||||
|
|
||||||
|
for (int i = 0; i < hex_count; i++) |
||||||
|
fillTriangleBufferFromHex(vbuf, 54 * i, (*hexes)[i]); |
||||||
|
|
||||||
|
|
||||||
|
if (!initGLBufferObject(&g_filled_hex_render_group.vertex_buffer, vbuf_len, GL_DYNAMIC_DRAW, vbuf)) |
||||||
|
return false; |
||||||
|
|
||||||
|
// color data for hex vertices
|
||||||
|
|
||||||
|
fillColorBuffer(cbuf, vbuf_len, hexes); |
||||||
|
|
||||||
|
if (!initGLBufferObject(&g_filled_hex_render_group.color_buffer, vbuf_len, GL_DYNAMIC_DRAW, cbuf)) |
||||||
|
return false; |
||||||
|
|
||||||
|
// hex line vertex data
|
||||||
|
|
||||||
|
fillHexLineBuffer(line_buf, line_buf_len, hexes); |
||||||
|
if (!initGLBufferObject(&g_hex_line_render_group.vertex_buffer, line_buf_len, GL_STATIC_DRAW, line_buf)) |
||||||
|
return false; |
||||||
|
|
||||||
|
// free temporary buffers
|
||||||
|
// TODO: temp buffers won't be freed if exit early is hit
|
||||||
|
std::free(vbuf); |
||||||
|
std::free(cbuf); |
||||||
|
std::free(line_buf); |
||||||
|
vbuf = cbuf = line_buf = nullptr; |
||||||
|
|
||||||
|
// debug draw vertexes
|
||||||
|
|
||||||
|
// 4 vertices, 3 floats per vertex
|
||||||
|
int len = 4 * 3; |
||||||
|
if (!initGLBufferObject(&g_debug_render_group.vertex_buffer, len, GL_DYNAMIC_DRAW, 0)) |
||||||
|
return false; |
||||||
|
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// TODO: Testing Entities/assimp model loading
|
||||||
|
|
||||||
|
g_entity_render_group.entity = &entities[0]; |
||||||
|
entities[0].model_transform = glm::scale(glm::mat4(1), glm::vec3(100, 100, 100)); |
||||||
|
|
||||||
|
uint entity_buf_len = entities[0].num_vertices * 3; |
||||||
|
GLfloat* entity_buf = (GLfloat*) std::calloc(entity_buf_len, sizeof(GLfloat)); |
||||||
|
GLfloat* entity_color_buf = (GLfloat*) std::calloc(entity_buf_len, sizeof(GLfloat)); |
||||||
|
|
||||||
|
// dump vertices into temporary buffer
|
||||||
|
uint vertex_index = 0; |
||||||
|
uint vertex_prop_index = 0; |
||||||
|
GLfloat entity_test_color[3]; |
||||||
|
convertColor(entity_test_color, 0xF46000FF); |
||||||
|
|
||||||
|
for (uint j = 0; j < entity_buf_len; j++) { |
||||||
|
const glm::vec3& vertex = entities[0].vertices[vertex_index]; |
||||||
|
|
||||||
|
switch (vertex_prop_index) { |
||||||
|
case 0: entity_buf[j] = vertex.x; break; |
||||||
|
case 1: entity_buf[j] = vertex.y; break; |
||||||
|
case 2: entity_buf[j] = vertex.z; break; |
||||||
|
} |
||||||
|
|
||||||
|
entity_color_buf[j] = entity_test_color[vertex_prop_index]; |
||||||
|
vertex_prop_index++; |
||||||
|
|
||||||
|
if (vertex_prop_index == 3) { |
||||||
|
vertex_prop_index = 0; |
||||||
|
vertex_index++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!initGLBufferObject(&g_entity_render_group.vertex_buffer, entity_buf_len, |
||||||
|
GL_DYNAMIC_DRAW, entity_buf)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (!initGLBufferObject(&g_entity_render_group.color_buffer, entity_buf_len, |
||||||
|
GL_DYNAMIC_DRAW, entity_color_buf)) |
||||||
|
return false; |
||||||
|
|
||||||
|
std::free(entity_buf); |
||||||
|
std::free(entity_color_buf); |
||||||
|
entity_buf = entity_color_buf = nullptr; |
||||||
|
|
||||||
|
/////////
|
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
moveCamera(bool up, bool left, bool down, bool right, bool forward, bool backward) |
||||||
|
{ |
||||||
|
if (!up && !left && !down && !right && !forward && !backward) |
||||||
|
return; |
||||||
|
|
||||||
|
glm::vec3 f = g_camera.forward; |
||||||
|
glm::vec3 u = g_camera.up; |
||||||
|
glm::vec3 old = g_camera.position; |
||||||
|
glm::vec3 &p = g_camera.position; |
||||||
|
glm::vec3 v(0.f); // normalized direction
|
||||||
|
|
||||||
|
if (forward) v += f; |
||||||
|
if (backward) v -= f; |
||||||
|
if (up) v += u; |
||||||
|
if (down) v -= u; |
||||||
|
if (left) |
||||||
|
{ |
||||||
|
glm::vec3 l = glm::cross(f, u); |
||||||
|
v -= l; |
||||||
|
} |
||||||
|
if (right) |
||||||
|
{ |
||||||
|
glm::vec3 r = glm::cross(u, f); |
||||||
|
v -= r; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: this still doesn't fix side to side movement magnitude when vAngle is
|
||||||
|
// close to +- 90 degrees
|
||||||
|
glm::normalize(v); |
||||||
|
p += (v * MOVE_SPEED); |
||||||
|
glm::vec3 diff = old - p; |
||||||
|
g_scene_matrices.view = glm::translate(g_scene_matrices.view, diff); |
||||||
|
g_scene_matrices.MVP = g_scene_matrices.projection * g_scene_matrices.view * g_scene_matrices.model;
|
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
rotateCamera(int32 xrel, int32 yrel) |
||||||
|
{ |
||||||
|
camera &c = g_camera; |
||||||
|
float &h = c.hAngle; |
||||||
|
float &v = c.vAngle; |
||||||
|
h += ROTATE_SPEED * xrel; |
||||||
|
v -= ROTATE_SPEED * yrel; |
||||||
|
|
||||||
|
// clamp vAngle to prevent gimbal lock
|
||||||
|
float a = glm::radians(CAMERA_Z_CLAMP_ANGLE); |
||||||
|
if (v < (-1 * a)) v = (-1 * a); |
||||||
|
if (v > a) v = a; |
||||||
|
|
||||||
|
c.forward = glm::vec3( |
||||||
|
glm::cos(v) * glm::sin(h), |
||||||
|
glm::cos(v) * glm::cos(h), |
||||||
|
glm::sin(v) |
||||||
|
); |
||||||
|
|
||||||
|
c.up = glm::vec3(0,0,1); |
||||||
|
c.left = glm::cross(c.forward, c.up); |
||||||
|
|
||||||
|
g_scene_matrices.view = glm::lookAt(c.position, c.position + c.forward, c.up); |
||||||
|
g_scene_matrices.MVP = g_scene_matrices.projection * g_scene_matrices.view * g_scene_matrices.model;
|
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: don't need this yet
|
||||||
|
void |
||||||
|
rollCamera(bool CW, bool CCW) |
||||||
|
{ |
||||||
|
#if 0 |
||||||
|
if ((!CW && !CCW) || (CW && CCW)) |
||||||
|
return; |
||||||
|
|
||||||
|
float a = 0.005f; |
||||||
|
if (CW) a *= 1; |
||||||
|
if (CCW) a *= -1; |
||||||
|
camera &c = g_camera; |
||||||
|
glm::mat4 m = glm::rotate(glm::mat4(1.f), a, c.forward); |
||||||
|
glm::vec4 v(c.up.x, c.up.y, c.up.z, 0); |
||||||
|
v = v * m; |
||||||
|
g_camera.up = glm::vec3(v.x, v.y, v.z); |
||||||
|
g_scene_matrices.view *= m; |
||||||
|
g_scene_matrices.MVP = g_scene_matrices.projection * g_scene_matrices.view * g_scene_matrices.model;
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
drawRenderGroup(gl_render_group* rg, GLenum draw_mode, bool update_vertex_data = false) |
||||||
|
{ |
||||||
|
glUseProgram(rg->program_id); |
||||||
|
|
||||||
|
// Send our transformation to the currently bound shader
|
||||||
|
glm::mat4 model_matrix; |
||||||
|
if (rg->entity) |
||||||
|
model_matrix = rg->entity->model_transform; |
||||||
|
else |
||||||
|
model_matrix = g_scene_matrices.model; |
||||||
|
|
||||||
|
glUniformMatrix4fv(rg->model_matrix_id, 1, GL_FALSE, &model_matrix[0][0]); |
||||||
|
glUniformMatrix4fv(rg->view_matrix_id, 1, GL_FALSE, &g_scene_matrices.view[0][0]); |
||||||
|
glUniformMatrix4fv(rg->projection_matrix_id, 1, GL_FALSE, &g_scene_matrices.projection[0][0]); |
||||||
|
|
||||||
|
// 1st attribute buffer : vertices
|
||||||
|
glEnableVertexAttribArray(0); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, rg->vertex_buffer.buffer_id); |
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); |
||||||
|
|
||||||
|
if (update_vertex_data) |
||||||
|
{ |
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->vertex_buffer.buffer_len * sizeof(GLfloat), |
||||||
|
rg->vertex_buffer.buffer); |
||||||
|
} |
||||||
|
|
||||||
|
// 2nd attribute buffer : colors
|
||||||
|
if (rg->color_buffer.buffer) |
||||||
|
{ |
||||||
|
glEnableVertexAttribArray(1); |
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, rg->color_buffer.buffer_id); |
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*) 0); |
||||||
|
|
||||||
|
// TODO: maybe add an option to not send color data every frame?
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, rg->color_buffer.buffer_len * sizeof(GLfloat), |
||||||
|
rg->color_buffer.buffer); |
||||||
|
} |
||||||
|
// draw
|
||||||
|
glDrawArrays(draw_mode, 0, rg->vertex_buffer.buffer_len / 3); |
||||||
|
|
||||||
|
// cleanup
|
||||||
|
glDisableVertexAttribArray(0); |
||||||
|
if (rg->color_buffer.buffer) |
||||||
|
glDisableVertexAttribArray(1); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
renderFrame(std::vector<hex_info> *hexes) |
||||||
|
{ |
||||||
|
glClearColor(g_clear_col.R, g_clear_col.G, g_clear_col.B, g_clear_col.A); |
||||||
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
||||||
|
|
||||||
|
// filled hexes
|
||||||
|
|
||||||
|
// get new colors every frame
|
||||||
|
gl_render_group* rg = &g_filled_hex_render_group; |
||||||
|
fillColorBuffer(rg->color_buffer.buffer, rg->color_buffer.buffer_len, hexes); |
||||||
|
drawRenderGroup(rg, GL_TRIANGLES); |
||||||
|
|
||||||
|
// hex lines
|
||||||
|
|
||||||
|
drawRenderGroup(&g_hex_line_render_group, GL_LINES); |
||||||
|
|
||||||
|
// TODO: testing entity rendering
|
||||||
|
drawRenderGroup(&g_entity_render_group, GL_TRIANGLES); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
renderDebug(std::vector<Point> &vertices) |
||||||
|
{ |
||||||
|
// TODO: indexed line drawing
|
||||||
|
real64 buf[4 * 3] = { |
||||||
|
vertices[0].x, vertices[0].y, 0, |
||||||
|
vertices[1].x, vertices[1].y, 0, |
||||||
|
vertices[2].x, vertices[2].y, 0, |
||||||
|
vertices[3].x, vertices[3].y, 0, |
||||||
|
}; |
||||||
|
|
||||||
|
// copy vertexes to render group
|
||||||
|
gl_render_group* rg = &g_debug_render_group; |
||||||
|
for (int i = 0; i < 12; i++) |
||||||
|
rg->vertex_buffer.buffer[i] = buf[i]; |
||||||
|
|
||||||
|
drawRenderGroup(rg, GL_LINE_LOOP, true); |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
freeBuffers() |
||||||
|
{ |
||||||
|
std::vector<gl_render_group> groups = { |
||||||
|
g_filled_hex_render_group, |
||||||
|
g_hex_line_render_group, |
||||||
|
g_debug_render_group, |
||||||
|
//g_entity_render_group TODO: handle enitiy memory separately
|
||||||
|
}; |
||||||
|
|
||||||
|
for (gl_render_group group : groups) |
||||||
|
{ |
||||||
|
if (group.vertex_buffer.buffer) |
||||||
|
{ |
||||||
|
std::free(group.vertex_buffer.buffer); |
||||||
|
group.vertex_buffer.buffer = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (group.color_buffer.buffer) |
||||||
|
{ |
||||||
|
std::free(group.color_buffer.buffer); |
||||||
|
group.color_buffer.buffer = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue