source: opengl-game/new-game.cpp@ a23fc08

feature/imgui-sdl points-test
Last change on this file since a23fc08 was a23fc08, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 5 years ago

Move several functions from new-game.cpp to utils.cpp

  • Property mode set to 100644
File size: 87.4 KB
Line 
1#include "stb_image.h"
2
3// I think this was for the OpenGL 4 book font file tutorial
4//#define STB_IMAGE_WRITE_IMPLEMENTATION
5//#include "stb_image_write.h"
6
7#define _USE_MATH_DEFINES
8
9#include <glm/mat4x4.hpp>
10#include <glm/gtc/matrix_transform.hpp>
11#include <glm/gtc/type_ptr.hpp>
12
13#include "IMGUI/imgui.h"
14#include "imgui_impl_glfw_gl3.h"
15
16#include <GL/glew.h>
17#include <GLFW/glfw3.h>
18
19#include <cstdio>
20#include <cstdlib>
21#include <ctime>
22#include <iostream>
23#include <fstream>
24#include <sstream>
25#include <cmath>
26#include <string>
27#include <array>
28#include <vector>
29#include <queue>
30#include <map>
31
32#include "logger.h"
33#include "utils.h"
34
35#include "Compiler.h"
36
37#ifdef WINDOWS
38 #include <windows.h>
39 #include <excpt.h>
40 #include <io.h>
41
42 #include "FileStackWalker.h"
43#else
44 // TODO: Move as much Windows crash-logging stuff into CrashLogger as well
45 #include "CrashLogger.h"
46#endif
47
48using namespace std;
49using namespace glm;
50
51enum State {
52 STATE_MAIN_MENU,
53 STATE_GAME,
54};
55
56enum Event {
57 EVENT_GO_TO_MAIN_MENU,
58 EVENT_GO_TO_GAME,
59 EVENT_QUIT,
60};
61
62enum ObjectType {
63 TYPE_SHIP,
64 TYPE_ASTEROID,
65 TYPE_LASER,
66 TYPE_EXPLOSION,
67};
68
69enum AttribType {
70 ATTRIB_UNIFORM,
71 ATTRIB_OBJECT_VARYING,
72 ATTRIB_POINT_VARYING,
73};
74
75// Add more types as I need them
76enum UniformType {
77 UNIFORM_NONE,
78 UNIFORM_MATRIX_4F,
79 UNIFORM_1F,
80 UNIFORM_3F,
81};
82
83enum UIValueType {
84 UIVALUE_INT,
85 UIVALUE_DOUBLE,
86};
87
88struct SceneObject {
89 unsigned int id;
90 ObjectType type;
91 bool deleted;
92
93 // Currently, model_transform should only have translate, and rotation and scale need to be done in model_base since
94 // they need to be done when the object is at the origin. I should change this to have separate scale, rotate, and translate
95 // matrices for each object that can be updated independently and then applied to the object in that order.
96 // TODO: Actually, to make this as generic as possible, each object should have a matrix stack to support,
97 // for instance, applying a rotate, then a translate, then another rotate. Think about and implement the best approach.
98 mat4 model_mat, model_base, model_transform;
99 mat4 translate_mat; // beginning of doing what's mentioned above
100 unsigned int num_points;
101 GLuint vertex_vbo_offset;
102 GLuint ubo_offset;
103 vector<GLfloat> points;
104 vector<GLfloat> colors;
105 vector<GLfloat> texcoords;
106 vector<GLfloat> normals;
107 vec3 bounding_center;
108 GLfloat bounding_radius;
109};
110
111struct Asteroid : SceneObject {
112 float hp;
113};
114
115struct Laser : SceneObject {
116 Asteroid* targetAsteroid;
117};
118
119struct ParticleEffect : SceneObject {
120 vector<GLfloat> particleVelocities;
121 vector<GLfloat> particleTimes;
122 GLfloat startTime;
123 GLfloat duration;
124};
125
126struct EffectOverTime {
127 float& effectedValue;
128 float startValue;
129 double startTime;
130 float changePerSecond;
131 bool deleted;
132 SceneObject* effectedObject;
133
134 // TODO: Why not just use an initializer list for all the instance variables
135 // TODO: Maybe pass in startTime instead of calling glfwGetTime() here
136 EffectOverTime(float& effectedValue, float changePerSecond, SceneObject* object)
137 : effectedValue(effectedValue), changePerSecond(changePerSecond), effectedObject(object) {
138 startValue = effectedValue;
139 startTime = glfwGetTime();
140 deleted = false;
141 }
142};
143
144struct BufferInfo {
145 unsigned int ubo_base;
146 unsigned int ubo_offset;
147 unsigned int ubo_capacity;
148};
149
150struct AttribInfo {
151 AttribType attribType;
152 GLuint index;
153 GLint size;
154 GLenum type;
155 UniformType uniType;
156 GLuint buffer; // For uniforms, this is the uniform location
157 size_t fieldOffset;
158 GLfloat* data; // pointer to data source for uniform attributes
159};
160
161struct ShaderModelGroup {
162 GLuint shaderProgram;
163 GLuint vao;
164 map<string, AttribInfo> attribs;
165 unsigned int numPoints;
166 unsigned int vboCapacity;
167};
168
169struct UIValue {
170 UIValueType type;
171 string label;
172 void* value;
173
174 UIValue(UIValueType _type, string _label, void* _value) : type(_type), label(_label), value(_value) {}
175};
176
177void glfw_error_callback(int error, const char* description);
178
179void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
180void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
181void window_size_callback(GLFWwindow* window, int width, int height);
182
183void APIENTRY debugGlCallback(
184 GLenum source,
185 GLenum type,
186 GLuint id,
187 GLenum severity,
188 GLsizei length,
189 const GLchar* message,
190 const void* userParam
191);
192
193bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point);
194bool insideTriangle(vec3 p, array<vec3, 3> triangle_points);
195
196GLuint loadShader(GLenum type, string file);
197GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath);
198unsigned char* loadImage(string file_name, int* x, int* y);
199
200void initObject(SceneObject* obj);
201void addObjectToScene(SceneObject* obj,
202 map<GLuint, BufferInfo>& shaderBufferInfo,
203 map<ObjectType, ShaderModelGroup>& modelGroups,
204 GLuint ubo);
205void removeObjectFromScene(SceneObject& obj, GLuint ubo);
206
207ShaderModelGroup createModelGroup(GLuint shaderProgram);
208void removeModelFromGroup(ShaderModelGroup& modelGroup, SceneObject& model);
209void addModelToGroup(ShaderModelGroup& modelGroup, SceneObject& model);
210
211void defineModelGroupAttrib(ShaderModelGroup& modelGroup, string name, AttribType attribType, GLint size, GLenum type, size_t fieldOffset);
212void defineModelGroupUniform(ShaderModelGroup& modelGroup, string name, AttribType attribType, GLint size, UniformType type, GLfloat* data);
213void initModelGroupAttribs(ShaderModelGroup& modelGroup);
214void bindUniformData(AttribInfo& attrib);
215void bindUniformData(AttribInfo& attrib, GLfloat* data);
216
217size_t GLsizeof(GLenum);
218GLvoid* getVectorAttribPtr(SceneObject& obj, size_t attribOffset);
219GLvoid* getScalarAttribPtr(SceneObject& obj, size_t attribOffset);
220
221void calculateObjectBoundingBox(SceneObject* obj);
222
223void populateBuffers(vector<SceneObject*>& objects,
224 map<GLuint, BufferInfo>& shaderBufferInfo,
225 map<ObjectType, ShaderModelGroup>& modelGroups,
226 GLuint ubo);
227
228void copyObjectDataToBuffers(SceneObject& obj,
229 map<GLuint, BufferInfo>& shaderBufferInfo,
230 map<ObjectType, ShaderModelGroup>& modelGroups,
231 GLuint ubo);
232
233void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo);
234
235// TODO: instead of using these methods, create constructors for these
236SceneObject* createShip();
237Asteroid* createAsteroid(vec3 pos);
238Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width);
239ParticleEffect* createExplosion(mat4 model_mat);
240
241void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
242void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, ShaderModelGroup& laserSmg, GLuint asteroid_sp);
243bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection);
244
245void renderMainMenu();
246void renderMainMenuGui();
247
248void renderScene(map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo);
249void renderSceneGui(map<string, vector<UIValue>> valueLists);
250
251void initGuiValueLists(map<string, vector<UIValue>> valueLists);
252void renderGuiValueList(vector<UIValue>& values);
253
254#define NUM_KEYS (512)
255#define ONE_DEG_IN_RAD ((2.0f * M_PI) / 360.0f) // 0.017444444 (maybe make this a const instead)
256#define TARGET_FPS 60.0f
257
258const int KEY_STATE_UNCHANGED = -1;
259const bool FULLSCREEN = false;
260const int EXPLOSION_PARTICLE_COUNT = 300;
261unsigned int MAX_UNIFORMS = 0; // Requires OpenGL constants only available at runtime, so it can't be const
262
263int key_state[NUM_KEYS];
264bool key_down[NUM_KEYS];
265
266int windowWidth = 640;
267int windowHeight = 480;
268
269vec3 cam_pos;
270
271mat4 view_mat;
272mat4 proj_mat;
273
274vector<SceneObject*> objects;
275queue<Event> events;
276vector<EffectOverTime*> effects;
277
278SceneObject* clickedObject = NULL;
279SceneObject* selectedObject = NULL;
280
281float NEAR_CLIP = 0.1f;
282float FAR_CLIP = 100.0f;
283
284// TODO: Should really have some array or struct of UI-related variables
285bool isRunning = true;
286
287ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
288
289Laser* leftLaser = NULL;
290EffectOverTime* leftLaserEffect = NULL;
291
292Laser* rightLaser = NULL;
293EffectOverTime* rightLaserEffect = NULL;
294
295map<string, vector<UIValue>> valueLists;
296
297/*
298* TODO: Asteroid and ship movement currently depend on framerate, fix this in a generic/reusable way
299* Disabling vsync is a great way to test this
300*/
301
302CrashLogger logger;
303
304// Helps to test logging during crashes
305/*
306void badFunc() {
307 int* test = NULL;
308 *test = 1;
309}
310*/
311
312#ifdef WINDOWS
313
314// Give it a file handle to crash.log instead (using the code in CrashLogger.cpp)
315FileStackWalker sw(2);
316
317bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread);
318#endif
319
320int main(int argc, char* argv[]) {
321#ifdef WINDOWS
322 __try {
323 __main(argc, argv);
324 // maybe do this and call a function inside CrashLogger
325 // In that case, also pass GetCurrentThread() as a parameter to then pass to handleException
326 // I could also move almost all of this into CrashLogger by creating a function in CrashLogger that takes a reference
327 // to the effective main function and, for Windows, wraps it in all this error-handling stuff
328 } __except( handleException(GetExceptionCode(), GetExceptionInformation(), GetCurrentThread()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER) {
329 _write(2, "CAUGHT\n", 7);
330 }
331
332 exit(0);
333}
334
335int __main(int argc, char* argv[]) {
336#endif
337 cout << "New OpenGL Game" << endl;
338
339 restart_gl_log();
340 gl_log("starting GLFW\n%s", glfwGetVersionString());
341
342 open_log();
343 get_log() << "starting GLFW" << endl;
344 get_log() << glfwGetVersionString() << endl;
345
346 glfwSetErrorCallback(glfw_error_callback);
347 if (!glfwInit()) {
348 gl_log_err("ERROR: could not start GLFW3");
349 cerr << "ERROR: could not start GLFW3" << endl;
350 get_log() << "ERROR: could not start GLFW3" << endl;
351 return 1;
352 }
353
354#ifdef MAC
355 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
356 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
357 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
358 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
359#else
360 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
361 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
362#endif
363
364 GLFWwindow* window = NULL;
365 GLFWmonitor* mon = NULL;
366
367 glfwWindowHint(GLFW_SAMPLES, 16);
368 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
369
370 if (FULLSCREEN) {
371 mon = glfwGetPrimaryMonitor();
372 const GLFWvidmode* vmode = glfwGetVideoMode(mon);
373
374 windowWidth = vmode->width;
375 windowHeight = vmode->height;
376 cout << "Fullscreen resolution " << vmode->width << "x" << vmode->height << endl;
377 }
378 window = glfwCreateWindow(windowWidth, windowHeight, "New OpenGL Game", mon, NULL);
379
380 if (!window) {
381 gl_log_err("ERROR: could not open window with GLFW3");
382 cerr << "ERROR: could not open window with GLFW3" << endl;
383 get_log() << "ERROR: could not open window with GLFW3" << endl;
384 glfwTerminate();
385 return 1;
386 }
387
388 glfwMakeContextCurrent(window);
389 glViewport(0, 0, windowWidth, windowHeight);
390
391 glewExperimental = GL_TRUE;
392 glewInit();
393
394 if (GLEW_KHR_debug) {
395 cout << "FOUND GLEW debug extension" << endl;
396 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
397 glDebugMessageCallback((GLDEBUGPROC)debugGlCallback, nullptr);
398 cout << "Bound debug callback" << endl;
399 } else {
400 cout << "OpenGL debug message callback is not supported" << endl;
401 }
402
403 srand(time(0));
404
405 /*
406 * RENDERING ALGORITHM NOTES:
407 *
408 * Basically, I need to split my objects into groups, so that each group fits into
409 * GL_MAX_UNIFORM_BLOCK_SIZE. I need to have an offset and a size for each group.
410 * Getting the offset is straitforward. The size may as well be GL_MAX_UNIFORM_BLOCK_SIZE
411 * for each group, since it seems that smaller sizes just round up to the nearest GL_MAX_UNIFORM_BLOCK_SIZE
412 *
413 * I'll need to have a loop inside my render loop that calls glBindBufferRange(GL_UNIFORM_BUFFER, ...
414 * for every 1024 objects and then draws all those objects with one glDraw call.
415 *
416 * Since I currently have very few objects, I'll wait to implement this until I have
417 * a reasonable number of objects always using the same shader.
418 */
419
420 GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT, MAX_UNIFORM_BLOCK_SIZE;
421 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &UNIFORM_BUFFER_OFFSET_ALIGNMENT);
422 glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &MAX_UNIFORM_BLOCK_SIZE);
423
424 MAX_UNIFORMS = MAX_UNIFORM_BLOCK_SIZE / sizeof(mat4);
425
426 cout << "UNIFORM_BUFFER_OFFSET_ALIGNMENT: " << UNIFORM_BUFFER_OFFSET_ALIGNMENT << endl;
427 cout << "MAX_UNIFORMS: " << MAX_UNIFORMS << endl;
428
429 // Setup Dear ImGui binding
430 IMGUI_CHECKVERSION();
431 ImGui::CreateContext();
432 ImGuiIO& io = ImGui::GetIO(); (void)io;
433 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
434 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
435 ImGui_ImplGlfwGL3_Init(window, true);
436
437 // Setup style
438 ImGui::StyleColorsDark();
439 //ImGui::StyleColorsClassic();
440
441 glfwSetMouseButtonCallback(window, mouse_button_callback);
442 glfwSetKeyCallback(window, key_callback);
443
444 glfwSetWindowSizeCallback(window, window_size_callback);
445
446 const GLubyte* renderer = glGetString(GL_RENDERER);
447 const GLubyte* version = glGetString(GL_VERSION);
448 cout << "Renderer: " << renderer << endl;
449 cout << "Supported OpenGL version: " << version << endl;
450
451 gl_log("Renderer: %s", renderer);
452 gl_log("Supported OpenGL version: %s", version);
453
454 get_log() << "Renderer: " << renderer << endl;
455 get_log() << "Supported OpenGL version: " << version << endl;
456
457 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
458
459 glEnable(GL_DEPTH_TEST);
460 glDepthFunc(GL_LESS);
461
462 glEnable(GL_CULL_FACE);
463 // glCullFace(GL_BACK);
464 // glFrontFace(GL_CW);
465
466 int x, y;
467 unsigned char* texImage = loadImage("laser.png", &x, &y);
468 if (texImage) {
469 cout << "Laser texture loaded successfully!" << endl;
470 cout << x << ", " << y << endl;
471 cout << "first 4 bytes are: " << texImage[0] << " " << texImage[1] << " " << texImage[2] << " " << texImage[3] << endl;
472 }
473
474 GLuint laserTex = 0;
475 glGenTextures(1, &laserTex);
476 glActiveTexture(GL_TEXTURE0);
477 glBindTexture(GL_TEXTURE_2D, laserTex);
478 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
479
480 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
481 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
482 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
483 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
484
485 /* RENDERING ALGORITHM
486 *
487 * Create a separate vbo for each of the following things:
488 * - points
489 * - colors
490 * - texture coordinates
491 * - selected colors
492 * - normals
493 * - indices into a ubo that stores a model matrix for each object
494 *
495 * Also, make a model matrix ubo, the entirety of which will be passed to the vertex shader.
496 * The vbo containing the correct index into the ubo (mentioned above) will be used to select
497 * the right model matrix for each point. The index in the vbo will be the saem for all points
498 * of any given object.
499 *
500 * Right now, the currently selected object is drawn using one color (specified in the selected
501 * colors vbo) regardless of whether it is normally rendering using colors or a texture. The selected
502 * object is rendering by binding the selected colors vbo in place of the colors vbo and using the colors
503 * shader. Then, the selected object is redrawn along with all other objects, but the depth buffer test
504 * prevents the unselected version of the object from appearing on the screen. This lets me render all the
505 * objects that use a particular shader using one glDrawArrays() call.
506 */
507
508 GLfloat laserColor[3] = {0.2f, 1.0f, 0.2f};
509 GLfloat curTime, prevTime, elapsedTime;
510
511 GLuint ubo = 0;
512 glGenBuffers(1, &ubo);
513
514 map<GLuint, BufferInfo> shaderBufferInfo;
515 map<ObjectType, ShaderModelGroup> modelGroups;
516
517 modelGroups[TYPE_SHIP] = createModelGroup(
518 loadShaderProgram("./ship.vert", "./ship.frag"));
519 shaderBufferInfo[modelGroups[TYPE_SHIP].shaderProgram] = BufferInfo(); // temporary
520
521 defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_position", ATTRIB_POINT_VARYING,
522 3, GL_FLOAT, offset_of(&SceneObject::points));
523 defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_color", ATTRIB_POINT_VARYING,
524 3, GL_FLOAT, offset_of(&SceneObject::colors));
525 defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_normal", ATTRIB_POINT_VARYING,
526 3, GL_FLOAT, offset_of(&SceneObject::normals));
527 defineModelGroupAttrib(modelGroups[TYPE_SHIP], "ubo_index", ATTRIB_OBJECT_VARYING,
528 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
529
530 defineModelGroupUniform(modelGroups[TYPE_SHIP], "view", ATTRIB_UNIFORM,
531 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
532 defineModelGroupUniform(modelGroups[TYPE_SHIP], "proj", ATTRIB_UNIFORM,
533 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
534
535 initModelGroupAttribs(modelGroups[TYPE_SHIP]);
536
537 modelGroups[TYPE_ASTEROID] = createModelGroup(
538 loadShaderProgram("./asteroid.vert", "./asteroid.frag"));
539 shaderBufferInfo[modelGroups[TYPE_ASTEROID].shaderProgram] = BufferInfo(); // temporary
540
541 defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_position", ATTRIB_POINT_VARYING,
542 3, GL_FLOAT, offset_of(&SceneObject::points));
543 defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_color", ATTRIB_POINT_VARYING,
544 3, GL_FLOAT, offset_of(&SceneObject::colors));
545 defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_normal", ATTRIB_POINT_VARYING,
546 3, GL_FLOAT, offset_of(&SceneObject::normals));
547 defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "ubo_index", ATTRIB_OBJECT_VARYING,
548 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
549
550 defineModelGroupUniform(modelGroups[TYPE_ASTEROID], "view", ATTRIB_UNIFORM,
551 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
552 defineModelGroupUniform(modelGroups[TYPE_ASTEROID], "proj", ATTRIB_UNIFORM,
553 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
554
555 initModelGroupAttribs(modelGroups[TYPE_ASTEROID]);
556
557 modelGroups[TYPE_LASER] = createModelGroup(
558 loadShaderProgram("./laser.vert", "./laser.frag"));
559 shaderBufferInfo[modelGroups[TYPE_LASER].shaderProgram] = BufferInfo(); // temporary
560
561 defineModelGroupAttrib(modelGroups[TYPE_LASER], "vertex_position", ATTRIB_POINT_VARYING,
562 3, GL_FLOAT, offset_of(&SceneObject::points));
563 defineModelGroupAttrib(modelGroups[TYPE_LASER], "vt", ATTRIB_POINT_VARYING,
564 2, GL_FLOAT, offset_of(&SceneObject::texcoords));
565 defineModelGroupAttrib(modelGroups[TYPE_LASER], "ubo_index", ATTRIB_OBJECT_VARYING,
566 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
567
568 defineModelGroupUniform(modelGroups[TYPE_LASER], "view", ATTRIB_UNIFORM,
569 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
570 defineModelGroupUniform(modelGroups[TYPE_LASER], "proj", ATTRIB_UNIFORM,
571 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
572 defineModelGroupUniform(modelGroups[TYPE_LASER], "laser_color", ATTRIB_UNIFORM,
573 1, UNIFORM_3F, laserColor);
574
575 initModelGroupAttribs(modelGroups[TYPE_LASER]);
576
577 modelGroups[TYPE_EXPLOSION] = createModelGroup(
578 loadShaderProgram("./explosion.vert", "./explosion.frag"));
579 shaderBufferInfo[modelGroups[TYPE_EXPLOSION].shaderProgram] = BufferInfo(); // temporary
580
581 defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "v_i", ATTRIB_POINT_VARYING,
582 3, GL_FLOAT, offset_of(&ParticleEffect::particleVelocities));
583 defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "start_time", ATTRIB_POINT_VARYING,
584 1, GL_FLOAT, offset_of(&ParticleEffect::particleTimes));
585 defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "ubo_index", ATTRIB_OBJECT_VARYING,
586 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
587
588 defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "cur_time", ATTRIB_UNIFORM,
589 1, UNIFORM_1F, &curTime);
590 defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "view", ATTRIB_UNIFORM,
591 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
592 defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "proj", ATTRIB_UNIFORM,
593 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
594
595 initModelGroupAttribs(modelGroups[TYPE_EXPLOSION]);
596
597 cam_pos = vec3(0.0f, 0.0f, 2.0f);
598 float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
599 float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
600
601 // player ship
602 objects.push_back(createShip());
603
604 vector<SceneObject>::iterator obj_it;
605
606 populateBuffers(objects, shaderBufferInfo, modelGroups, ubo);
607
608 float cam_speed = 1.0f;
609 float cam_yaw_speed = 60.0f*ONE_DEG_IN_RAD;
610 float cam_pitch_speed = 60.0f*ONE_DEG_IN_RAD;
611
612 // glm::lookAt can create the view matrix
613 // glm::perspective can create the projection matrix
614
615 mat4 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
616 mat4 yaw_mat = rotate(mat4(1.0f), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
617 mat4 pitch_mat = rotate(mat4(1.0f), -cam_pitch, vec3(1.0f, 0.0f, 0.0f));
618 mat4 R = pitch_mat * yaw_mat;
619 view_mat = R*T;
620
621 // TODO: Create a function to construct the projection matrix
622 // (Maybe I should just use glm::perspective, after making sure it matches what I have now)
623 float fov = 67.0f * ONE_DEG_IN_RAD;
624 float aspect = (float)windowWidth / (float)windowHeight;
625
626 float range = tan(fov * 0.5f) * NEAR_CLIP;
627 float Sx = NEAR_CLIP / (range * aspect);
628 float Sy = NEAR_CLIP / range;
629 float Sz = -(FAR_CLIP + NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
630 float Pz = -(2.0f * FAR_CLIP * NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
631
632 float proj_arr[] = {
633 Sx, 0.0f, 0.0f, 0.0f,
634 0.0f, Sy, 0.0f, 0.0f,
635 0.0f, 0.0f, Sz, -1.0f,
636 0.0f, 0.0f, Pz, 0.0f,
637 };
638 proj_mat = make_mat4(proj_arr);
639
640 /* TODO: Fix the UBO binding code based on the following forum post (in order to support multiple ubos):
641 (Also, I bookmarked a great explanation of this under )
642
643 CHECK MY OpenGL BOOKMARK CALLED "Learn OpenGL: Advanced GLSL"
644
645 No, you're misunderstanding how this works. UBO binding works exactly like texture object binding.
646
647 The OpenGL context has a number of slots for binding UBOs. There are GL_MAX_UNIFORM_BUFFER_BINDINGS number of
648 slots for UBO binding.
649
650 Uniform Blocks in a program can be set to use one of the slots in the context. You do this by first querying
651 the block index using the block name (glGetUniformBlockIndex). This is similar to how you need to use
652 glGetUniformLocation in order to set a uniform's value with glUniform. Block indices, like uniform locations,
653 are specific to a program.
654
655 Once you have the block index, you use glUniformBlockBinding to set that specific program to use a particular
656 uniform buffer slot in the context.
657
658 Let's say you have a global UBO that you want to use for every program. To make using it easier, you want to
659 bind it just once.
660
661 So first, you pick a uniform buffer slot in the context, one that always will refer to this UBO. Let's say
662 you pick slot 8.
663
664 When you build a program object that may use this global uniform buffer, what you do is quite simple. First,
665 after linking the program, call glGetUniformBlockIndex(program, "NameOfGlobalUniformBlock"). If you get back
666 GL_INVALID_INDEX, then you know that the global uniform block isn't used in that program. Otherwise you get
667 back a block index.
668
669 If you got a valid block index, then you call glUniformBlockBinding(program, uniformBlockIndex, 8). Remember
670 that 8 is the uniform buffer context slot that we selected earlier. This causes this particular program to
671 use uniform buffer slot #8 to find the buffer for "NameOfGlobalUniformBlock".
672
673 Finally, to set the actual buffer in the context, call glBindBufferRange(GL_UNIFORM_BUFFER, 8,
674 bufferObjectName, offset, size);
675 */
676
677 GLuint ub_binding_point = 0;
678
679 GLuint ship_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_SHIP].shaderProgram, "models");
680
681 GLuint asteroid_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_ASTEROID].shaderProgram, "models");
682
683 GLuint laser_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_LASER].shaderProgram, "models");
684
685 GLuint explosion_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_EXPLOSION].shaderProgram, "models");
686
687
688 glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
689 bindUniformData(modelGroups[TYPE_SHIP].attribs["view"]);
690 bindUniformData(modelGroups[TYPE_SHIP].attribs["proj"]);
691
692 glUniformBlockBinding(modelGroups[TYPE_SHIP].shaderProgram, ship_sp_models_ub_index, ub_binding_point);
693 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
694
695
696 glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
697 bindUniformData(modelGroups[TYPE_ASTEROID].attribs["view"]);
698 bindUniformData(modelGroups[TYPE_ASTEROID].attribs["proj"]);
699
700 glUniformBlockBinding(modelGroups[TYPE_ASTEROID].shaderProgram, asteroid_sp_models_ub_index, ub_binding_point);
701 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
702
703
704 // may want to do initialization for basic_texture uniform here too
705 // Right now, I think I'm getting away without getting that uniform location because I'm only
706 // using one texture, so setting it to GL_TEXTURE0 once works
707 glUseProgram(modelGroups[TYPE_LASER].shaderProgram);
708 bindUniformData(modelGroups[TYPE_LASER].attribs["view"]);
709 bindUniformData(modelGroups[TYPE_LASER].attribs["proj"]);
710 bindUniformData(modelGroups[TYPE_LASER].attribs["laser_color"]);
711
712 glUniformBlockBinding(modelGroups[TYPE_LASER].shaderProgram, laser_sp_models_ub_index, ub_binding_point);
713 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
714
715
716 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
717 bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["view"]);
718 bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["proj"]);
719
720 glUniformBlockBinding(modelGroups[TYPE_EXPLOSION].shaderProgram, explosion_sp_models_ub_index, ub_binding_point);
721 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
722
723
724 double fps;
725 unsigned int score = 0;
726
727 bool cam_moved = false;
728
729 int frame_count = 0;
730 double elapsed_seconds_fps = 0.0f;
731 double elapsed_seconds_spawn = 0.0f;
732
733 prevTime = glfwGetTime();
734
735 // This draws wireframes. Useful for seeing separate faces and occluded objects.
736 //glPolygonMode(GL_FRONT, GL_LINE);
737
738 // disable vsync to see real framerate
739 //glfwSwapInterval(0);
740
741 State curState = STATE_MAIN_MENU;
742
743 initGuiValueLists(valueLists);
744
745 valueLists["stats value list"].push_back(UIValue(UIVALUE_INT, "Score", &score));
746 valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "FPS", &fps));
747
748 while (!glfwWindowShouldClose(window) && isRunning) {
749 curTime = glfwGetTime();
750 elapsedTime = curTime - prevTime;
751
752 // temporary code to get around vsync issue in OSX Sierra
753 if (elapsedTime < (1.0f / TARGET_FPS)) {
754 continue;
755 }
756
757 prevTime = curTime;
758
759 elapsed_seconds_fps += elapsedTime;
760 if (elapsed_seconds_fps > 0.25f) {
761 fps = (double)frame_count / elapsed_seconds_fps;
762
763 frame_count = 0;
764 elapsed_seconds_fps = 0.0f;
765 }
766
767 frame_count++;
768
769 // Handle events
770
771 clickedObject = NULL;
772
773 // reset the all key states to KEY_STATE_UNCHANGED (something the GLFW key callback can never return)
774 // so that GLFW_PRESS and GLFW_RELEASE are only detected once
775 // TODO: Change this if we ever need to act on GLFW_REPEAT (which is when a key is held down
776 // continuously for a period of time)
777 fill(key_state, key_state + NUM_KEYS, KEY_STATE_UNCHANGED);
778
779 glfwPollEvents();
780
781 while (!events.empty()) {
782 switch (events.front()) {
783 case EVENT_GO_TO_MAIN_MENU:
784 curState = STATE_MAIN_MENU;
785 break;
786 case EVENT_GO_TO_GAME:
787 curState = STATE_GAME;
788 break;
789 case EVENT_QUIT:
790 isRunning = false;
791 break;
792 }
793 events.pop();
794 }
795
796 if (curState == STATE_GAME) {
797
798 elapsed_seconds_spawn += elapsedTime;
799 if (elapsed_seconds_spawn > 0.5f) {
800 SceneObject* obj = createAsteroid(vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f)));
801 addObjectToScene(obj, shaderBufferInfo, modelGroups, ubo);
802
803 elapsed_seconds_spawn -= 0.5f;
804 }
805
806 /*
807 if (clickedObject == &objects[0]) {
808 selectedObject = &objects[0];
809 }
810 if (clickedObject == &objects[1]) {
811 selectedObject = &objects[1];
812 }
813 */
814
815 /*
816 if (key_state[GLFW_KEY_SPACE] == GLFW_PRESS) {
817 transformObject(objects[1], translate(mat4(1.0f), vec3(0.3f, 0.0f, 0.0f)), ubo);
818 }
819 if (key_down[GLFW_KEY_RIGHT]) {
820 transformObject(objects[2], translate(mat4(1.0f), vec3(0.01f, 0.0f, 0.0f)), ubo);
821 }
822 if (key_down[GLFW_KEY_LEFT]) {
823 transformObject(objects[2], translate(mat4(1.0f), vec3(-0.01f, 0.0f, 0.0f)), ubo);
824 }
825 */
826
827 if (key_down[GLFW_KEY_RIGHT]) {
828 transformObject(*objects[0], translate(mat4(1.0f), vec3(0.01f, 0.0f, 0.0f)), ubo);
829
830 if (leftLaser != NULL && !leftLaser->deleted) {
831 translateLaser(leftLaser, vec3(0.01f, 0.0f, 0.0f), ubo);
832 }
833 if (rightLaser != NULL && !rightLaser->deleted) {
834 translateLaser(rightLaser, vec3(0.01f, 0.0f, 0.0f), ubo);
835 }
836 }
837 if (key_down[GLFW_KEY_LEFT]) {
838 transformObject(*objects[0], translate(mat4(1.0f), vec3(-0.01f, 0.0f, 0.0f)), ubo);
839
840 if (leftLaser != NULL && !leftLaser->deleted) {
841 translateLaser(leftLaser, vec3(-0.01f, 0.0f, 0.0f), ubo);
842 }
843 if (rightLaser != NULL && !rightLaser->deleted) {
844 translateLaser(rightLaser, vec3(-0.01f, 0.0f, 0.0f), ubo);
845 }
846 }
847
848 if (key_state[GLFW_KEY_Z] == GLFW_PRESS) {
849 vec3 offset(objects[0]->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
850
851 leftLaser = createLaser(
852 vec3(-0.21f, -1.19f, 1.76f)+offset,
853 vec3(-0.21f, -1.19f, -3.0f)+offset,
854 vec3(0.0f, 1.0f, 0.0f), 0.03f);
855 addObjectToScene(leftLaser, shaderBufferInfo, modelGroups, ubo);
856 } else if (key_state[GLFW_KEY_Z] == GLFW_RELEASE) {
857 removeObjectFromScene(*leftLaser, ubo);
858 }
859
860 if (key_state[GLFW_KEY_X] == GLFW_PRESS) {
861 vec3 offset(objects[0]->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
862
863 rightLaser = createLaser(
864 vec3(0.21f, -1.19f, 1.76f) + offset,
865 vec3(0.21f, -1.19f, -3.0f) + offset,
866 vec3(0.0f, 1.0f, 0.0f), 0.03f);
867 addObjectToScene(rightLaser, shaderBufferInfo, modelGroups, ubo);
868 } else if (key_state[GLFW_KEY_X] == GLFW_RELEASE) {
869 removeObjectFromScene(*rightLaser, ubo);
870 }
871
872 // this code moves the asteroids
873 for (unsigned int i = 0; i < objects.size(); i++) {
874 if (!objects[i]->deleted) {
875 if (objects[i]->type == TYPE_ASTEROID) {
876 transformObject(*objects[i], translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.04f)), ubo);
877
878 vec3 obj_center = vec3(view_mat * vec4(objects[i]->bounding_center, 1.0f));
879
880 if ((obj_center.z - objects[i]->bounding_radius) > -NEAR_CLIP) {
881 removeObjectFromScene(*objects[i], ubo);
882 }
883 if (((Asteroid*)objects[i])->hp <= 0) {
884 // TODO: Optimize this so I don't recalculate the camera rotation every time
885 float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
886 mat4 pitch_mat = rotate(mat4(1.0f), cam_pitch, vec3(1.0f, 0.0f, 0.0f));
887 mat4 model_mat = translate(mat4(1.0f), objects[i]->bounding_center) * pitch_mat;
888
889 removeObjectFromScene(*objects[i], ubo);
890 score++;
891
892 addObjectToScene(createExplosion(model_mat), shaderBufferInfo, modelGroups, ubo);
893 }
894 } else if (objects[i]->type == TYPE_EXPLOSION) {
895 ParticleEffect* explosion = (ParticleEffect*)objects[i];
896 if (glfwGetTime() >= explosion->startTime + explosion->duration) {
897 removeObjectFromScene(*objects[i], ubo);
898 }
899 }
900 }
901 }
902
903 if (leftLaser != NULL && !leftLaser->deleted) {
904 updateLaserTarget(leftLaser, objects, modelGroups[TYPE_LASER], modelGroups[TYPE_ASTEROID].shaderProgram);
905 }
906 if (rightLaser != NULL && !rightLaser->deleted) {
907 updateLaserTarget(rightLaser, objects, modelGroups[TYPE_LASER], modelGroups[TYPE_ASTEROID].shaderProgram);
908 }
909 }
910
911 for (vector<EffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
912 if ((*it)->deleted || (*it)->effectedObject->deleted) {
913 delete *it;
914 it = effects.erase(it);
915 } else {
916 EffectOverTime* eot = *it;
917 eot->effectedValue = eot->startValue + (curTime - eot->startTime) * eot->changePerSecond;
918
919 it++;
920 }
921 }
922
923 if (key_state[GLFW_KEY_ESCAPE] == GLFW_PRESS) {
924 glfwSetWindowShouldClose(window, 1);
925 }
926
927 float dist = cam_speed * elapsedTime;
928 if (key_down[GLFW_KEY_A]) {
929 vec3 dir = vec3(inverse(R) * vec4(-1.0f, 0.0f, 0.0f, 1.0f));
930 cam_pos += dir * dist;
931
932 cam_moved = true;
933 }
934 if (key_down[GLFW_KEY_D]) {
935 vec3 dir = vec3(inverse(R) * vec4(1.0f, 0.0f, 0.0f, 1.0f));
936 cam_pos += dir * dist;
937
938 cam_moved = true;
939 }
940 if (key_down[GLFW_KEY_W]) {
941 vec3 dir = vec3(inverse(R) * vec4(0.0f, 0.0f, -1.0f, 1.0f));
942 cam_pos += dir * dist;
943
944 cam_moved = true;
945 }
946 if (key_down[GLFW_KEY_S]) {
947 vec3 dir = vec3(inverse(R) * vec4(0.0f, 0.0f, 1.0f, 1.0f));
948 cam_pos += dir * dist;
949
950 cam_moved = true;
951 }
952 /*
953 if (key_down[GLFW_KEY_LEFT]) {
954 cam_yaw += cam_yaw_speed * elapsedTime;
955 cam_moved = true;
956 }
957 if (key_down[GLFW_KEY_RIGHT]) {
958 cam_yaw -= cam_yaw_speed * elapsedTime;
959 cam_moved = true;
960 }
961 if (key_down[GLFW_KEY_UP]) {
962 cam_pitch += cam_pitch_speed * elapsedTime;
963 cam_moved = true;
964 }
965 if (key_down[GLFW_KEY_DOWN]) {
966 cam_pitch -= cam_pitch_speed * elapsedTime;
967 cam_moved = true;
968 }
969 */
970 if (cam_moved && false) { // disable camera movement
971 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
972
973 mat4 yaw_mat = rotate(mat4(1.0f), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
974 mat4 pitch_mat = rotate(mat4(1.0f), -cam_pitch, vec3(1.0f, 0.0f, 0.0f));
975 R = pitch_mat * yaw_mat;
976
977 view_mat = R * T;
978
979 glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
980 bindUniformData(modelGroups[TYPE_SHIP].attribs["view"]);
981
982 glUseProgram(modelGroups[TYPE_LASER].shaderProgram);
983 bindUniformData(modelGroups[TYPE_LASER].attribs["view"]);
984
985 cam_moved = false;
986 }
987
988 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
989 bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["cur_time"]);
990
991 // Render scene
992
993 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
994
995 // Anton's book suggests placing this here, after glClear(). Check it's impact on framerate
996 //glViewport(0, 0, windowWidth, windowHeight);
997
998 switch (curState) {
999 case STATE_MAIN_MENU:
1000 renderMainMenu();
1001 renderMainMenuGui();
1002 break;
1003 case STATE_GAME:
1004 renderScene(modelGroups, ubo);
1005 renderSceneGui(valueLists);
1006 break;
1007 }
1008
1009 glfwSwapBuffers(window);
1010 }
1011
1012 ImGui_ImplGlfwGL3_Shutdown();
1013 ImGui::DestroyContext();
1014
1015 glfwDestroyWindow(window);
1016 glfwTerminate();
1017
1018 close_log();
1019
1020 // free memory
1021
1022 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
1023 delete *it;
1024 }
1025}
1026
1027#ifdef WINDOWS
1028bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread) {
1029 if (pExp != NULL) {
1030 sw.ShowCallstack(thread, pExp->ContextRecord);
1031 }
1032
1033 if (expCode == EXCEPTION_ACCESS_VIOLATION) {
1034 _write(2, "ACCESS VIOLATION\n", 17);
1035 }
1036
1037 return true;
1038}
1039#endif
1040
1041void glfw_error_callback(int error, const char* description) {
1042 gl_log_err("GLFW ERROR: code %i msg: %s", error, description);
1043 cerr << "GLFW ERROR: code " << error << " msg: " << description << endl;
1044 get_log() << "GLFW ERROR: code " << error << " msg: " << description << endl;
1045}
1046
1047void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
1048 double mouse_x, mouse_y;
1049 glfwGetCursorPos(window, &mouse_x, &mouse_y);
1050
1051 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
1052 cout << "Mouse clicked (" << mouse_x << "," << mouse_y << ")" << endl;
1053 selectedObject = NULL;
1054
1055 float x = (2.0f*mouse_x) / windowWidth - 1.0f;
1056 float y = 1.0f - (2.0f*mouse_y) / windowHeight;
1057
1058 cout << "x: " << x << ", y: " << y << endl;
1059
1060 vec4 ray_clip = vec4(x, y, -1.0f, 1.0f);
1061 vec4 ray_eye = inverse(proj_mat) * ray_clip;
1062 ray_eye = vec4(vec2(ray_eye), -1.0f, 1.0f);
1063 vec4 ray_world = inverse(view_mat) * ray_eye;
1064
1065 vec4 click_point;
1066 vec3 closest_point = vec3(0.0f, 0.0f, -FAR_CLIP); // Any valid point will be closer than the far clipping plane, so initial value to that
1067 SceneObject* closest_object = NULL;
1068
1069 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
1070 if ((*it)->type == TYPE_LASER) continue;
1071 for (unsigned int p_idx = 0; p_idx < (*it)->points.size(); p_idx += 9) {
1072 if (faceClicked(
1073 {
1074 vec3((*it)->points[p_idx], (*it)->points[p_idx + 1], (*it)->points[p_idx + 2]),
1075 vec3((*it)->points[p_idx + 3], (*it)->points[p_idx + 4], (*it)->points[p_idx + 5]),
1076 vec3((*it)->points[p_idx + 6], (*it)->points[p_idx + 7], (*it)->points[p_idx + 8]),
1077 },
1078 *it, ray_world, vec4(cam_pos, 1.0f), click_point
1079 )) {
1080 click_point = view_mat * click_point;
1081
1082 if (-NEAR_CLIP >= click_point.z && click_point.z > -FAR_CLIP && click_point.z > closest_point.z) {
1083 closest_point = vec3(click_point);
1084 closest_object = *it;
1085 }
1086 }
1087 }
1088 }
1089
1090 if (closest_object == NULL) {
1091 cout << "No object was clicked" << endl;
1092 } else {
1093 clickedObject = closest_object;
1094 cout << "Clicked object: " << clickedObject->id << endl;
1095 }
1096 }
1097}
1098
1099void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
1100 key_state[key] = action;
1101
1102 // should be true for GLFW_PRESS and GLFW_REPEAT
1103 key_down[key] = (action != GLFW_RELEASE);
1104}
1105
1106void window_size_callback(GLFWwindow* window, int width, int height) {
1107 cout << "Window resized to (" << width << ", " << height << ")" << endl;
1108
1109 windowWidth = width;
1110 windowHeight = height;
1111
1112 // TODO: Ideally, remove the window title bar when the window is maximized
1113 // Check https://github.com/glfw/glfw/issues/778
1114
1115 // This requires glfw3.3. I think I have to upgrade
1116 // Doesn't seem to be needed in OSX and also causes a segfault there
1117 //glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE);
1118}
1119
1120void APIENTRY debugGlCallback(
1121 GLenum source,
1122 GLenum type,
1123 GLuint id,
1124 GLenum severity,
1125 GLsizei length,
1126 const GLchar* message,
1127 const void* userParam
1128) {
1129 string strMessage(message);
1130
1131 // TODO: Use C++ strings directly
1132 char source_str[2048];
1133 char type_str[2048];
1134 char severity_str[2048];
1135
1136 switch (source) {
1137 case 0x8246:
1138 strcpy(source_str, "API");
1139 break;
1140 case 0x8247:
1141 strcpy(source_str, "WINDOW_SYSTEM");
1142 break;
1143 case 0x8248:
1144 strcpy(source_str, "SHADER_COMPILER");
1145 break;
1146 case 0x8249:
1147 strcpy(source_str, "THIRD_PARTY");
1148 break;
1149 case 0x824A:
1150 strcpy(source_str, "APPLICATION");
1151 break;
1152 case 0x824B:
1153 strcpy(source_str, "OTHER");
1154 break;
1155 default:
1156 strcpy(source_str, "undefined");
1157 break;
1158 }
1159
1160 switch (type) {
1161 case 0x824C:
1162 strcpy(type_str, "ERROR");
1163 break;
1164 case 0x824D:
1165 strcpy(type_str, "DEPRECATED_BEHAVIOR");
1166 break;
1167 case 0x824E:
1168 strcpy(type_str, "UNDEFINED_BEHAVIOR");
1169 break;
1170 case 0x824F:
1171 strcpy(type_str, "PORTABILITY");
1172 break;
1173 case 0x8250:
1174 strcpy(type_str, "PERFORMANCE");
1175 break;
1176 case 0x8251:
1177 strcpy(type_str, "OTHER");
1178 break;
1179 case 0x8268:
1180 strcpy(type_str, "MARKER");
1181 break;
1182 case 0x8269:
1183 strcpy(type_str, "PUSH_GROUP");
1184 break;
1185 case 0x826A:
1186 strcpy(type_str, "POP_GROUP");
1187 break;
1188 default:
1189 strcpy(type_str, "undefined");
1190 break;
1191 }
1192 switch (severity) {
1193 case 0x9146:
1194 strcpy(severity_str, "HIGH");
1195 break;
1196 case 0x9147:
1197 strcpy(severity_str, "MEDIUM");
1198 break;
1199 case 0x9148:
1200 strcpy(severity_str, "LOW");
1201 break;
1202 case 0x826B:
1203 strcpy(severity_str, "NOTIFICATION");
1204 break;
1205 default:
1206 strcpy(severity_str, "undefined");
1207 break;
1208 }
1209
1210 if (string(severity_str) != "NOTIFICATION") {
1211 cout << "OpenGL Error!!!" << endl;
1212 cout << "Source: " << string(source_str) << endl;
1213 cout << "Type: " << string(type_str) << endl;
1214 cout << "Severity: " << string(severity_str) << endl;
1215 cout << strMessage << endl;
1216 }
1217}
1218
1219
1220GLuint loadShader(GLenum type, string file) {
1221 cout << "Loading shader from file " << file << endl;
1222
1223 ifstream shaderFile(file);
1224 GLuint shaderId = 0;
1225
1226 if (shaderFile.is_open()) {
1227 string line, shaderString;
1228
1229 while(getline(shaderFile, line)) {
1230 shaderString += line + "\n";
1231 }
1232 shaderFile.close();
1233 const char* shaderCString = shaderString.c_str();
1234
1235 shaderId = glCreateShader(type);
1236 glShaderSource(shaderId, 1, &shaderCString, NULL);
1237 glCompileShader(shaderId);
1238
1239 cout << "Loaded successfully" << endl;
1240 } else {
1241 cout << "Failed to load the file" << endl;
1242 }
1243
1244 return shaderId;
1245}
1246
1247GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath) {
1248 GLuint vs = loadShader(GL_VERTEX_SHADER, vertexShaderPath);
1249 GLuint fs = loadShader(GL_FRAGMENT_SHADER, fragmentShaderPath);
1250
1251 GLuint shader_program = glCreateProgram();
1252 glAttachShader(shader_program, vs);
1253 glAttachShader(shader_program, fs);
1254
1255 glLinkProgram(shader_program);
1256
1257 return shader_program;
1258}
1259
1260unsigned char* loadImage(string file_name, int* x, int* y) {
1261 int n;
1262 int force_channels = 4; // This forces RGBA (4 bytes per pixel)
1263 unsigned char* image_data = stbi_load(file_name.c_str(), x, y, &n, force_channels);
1264
1265 int width_in_bytes = *x * 4;
1266 unsigned char *top = NULL;
1267 unsigned char *bottom = NULL;
1268 unsigned char temp = 0;
1269 int half_height = *y / 2;
1270
1271 // flip image upside-down to account for OpenGL treating lower-left as (0, 0)
1272 for (int row = 0; row < half_height; row++) {
1273 top = image_data + row * width_in_bytes;
1274 bottom = image_data + (*y - row - 1) * width_in_bytes;
1275 for (int col = 0; col < width_in_bytes; col++) {
1276 temp = *top;
1277 *top = *bottom;
1278 *bottom = temp;
1279 top++;
1280 bottom++;
1281 }
1282 }
1283
1284 if (!image_data) {
1285 gl_log_err("ERROR: could not load %s", file_name.c_str());
1286 cerr << "ERROR: could not load " << file_name << endl;
1287 get_log() << "ERROR: could not load " << file_name << endl;
1288 }
1289
1290 // Not Power-of-2 check
1291 if ((*x & (*x - 1)) != 0 || (*y & (*y - 1)) != 0) {
1292 gl_log_err("WARNING: texture %s is not power-of-2 dimensions", file_name.c_str());
1293 cerr << "WARNING: texture " << file_name << " is not power-of-2 dimensions" << endl;
1294 get_log() << "WARNING: texture " << file_name << " is not power-of-2 dimensions" << endl;
1295 }
1296
1297 return image_data;
1298}
1299
1300bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point) {
1301 // LINE EQUATION: P = O + Dt
1302 // O = cam
1303 // D = ray_world
1304
1305 // PLANE EQUATION: P dot n + d = 0
1306 // n is the normal vector
1307 // d is the offset from the origin
1308
1309 // Take the cross-product of two vectors on the plane to get the normal
1310 vec3 v1 = points[1] - points[0];
1311 vec3 v2 = points[2] - points[0];
1312
1313 vec3 normal = vec3(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
1314
1315 vec3 local_ray = vec3(inverse(obj->model_mat) * world_ray);
1316 vec3 local_cam = vec3(inverse(obj->model_mat) * cam);
1317
1318 local_ray = local_ray - local_cam;
1319
1320 float d = -glm::dot(points[0], normal);
1321 float t = -(glm::dot(local_cam, normal) + d) / glm::dot(local_ray, normal);
1322
1323 vec3 intersection = local_cam + t*local_ray;
1324
1325 if (insideTriangle(intersection, points)) {
1326 click_point = obj->model_mat * vec4(intersection, 1.0f);
1327 return true;
1328 } else {
1329 return false;
1330 }
1331}
1332
1333bool insideTriangle(vec3 p, array<vec3, 3> triangle_points) {
1334 vec3 v21 = triangle_points[1] - triangle_points[0];
1335 vec3 v31 = triangle_points[2] - triangle_points[0];
1336 vec3 pv1 = p - triangle_points[0];
1337
1338 float y = (pv1.y*v21.x - pv1.x*v21.y) / (v31.y*v21.x - v31.x*v21.y);
1339 float x = (pv1.x-y*v31.x) / v21.x;
1340
1341 return x > 0.0f && y > 0.0f && x+y < 1.0f;
1342}
1343
1344// TODO: Pass a reference, not a pointer
1345void initObject(SceneObject* obj) {
1346 obj->id = objects.size(); // currently unused
1347 obj->num_points = obj->points.size() / 3;
1348 obj->model_transform = mat4(1.0f);
1349 obj->deleted = false;
1350
1351 obj->normals.reserve(obj->points.size());
1352 for (unsigned int i = 0; i < obj->points.size(); i += 9) {
1353 vec3 point1 = vec3(obj->points[i], obj->points[i + 1], obj->points[i + 2]);
1354 vec3 point2 = vec3(obj->points[i + 3], obj->points[i + 4], obj->points[i + 5]);
1355 vec3 point3 = vec3(obj->points[i + 6], obj->points[i + 7], obj->points[i + 8]);
1356
1357 vec3 normal = normalize(cross(point2 - point1, point3 - point1));
1358
1359 // Add the same normal for all 3 points
1360 for (int j = 0; j < 3; j++) {
1361 obj->normals.push_back(normal.x);
1362 obj->normals.push_back(normal.y);
1363 obj->normals.push_back(normal.z);
1364 }
1365 }
1366
1367 if (obj->type == TYPE_SHIP || obj->type == TYPE_ASTEROID) {
1368 calculateObjectBoundingBox(obj);
1369
1370 obj->bounding_center = vec3(obj->translate_mat * vec4(obj->bounding_center, 1.0f));
1371 }
1372}
1373
1374// TODO: Check if I can pass in a reference to obj instead (do this for all other functions as well)
1375void addObjectToScene(SceneObject* obj,
1376 map<GLuint, BufferInfo>& shaderBufferInfo,
1377 map<ObjectType, ShaderModelGroup>& modelGroups,
1378 GLuint ubo) {
1379 objects.push_back(obj);
1380
1381 BufferInfo* bufferInfo = &shaderBufferInfo[modelGroups[obj->type].shaderProgram];
1382
1383 // Check if the buffers aren't large enough to fit the new object and, if so, call
1384 // populateBuffers() to resize and repopupulate them
1385 if ((modelGroups[obj->type].vboCapacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
1386 bufferInfo->ubo_capacity <= bufferInfo->ubo_offset)) {
1387
1388 if (leftLaser != NULL && leftLaser->deleted) {
1389 leftLaser = NULL;
1390 }
1391 if (rightLaser != NULL && rightLaser->deleted) {
1392 rightLaser = NULL;
1393 }
1394
1395 populateBuffers(objects, shaderBufferInfo, modelGroups, ubo);
1396 } else {
1397 copyObjectDataToBuffers(*objects.back(), shaderBufferInfo, modelGroups, ubo);
1398 }
1399}
1400
1401void removeObjectFromScene(SceneObject& obj, GLuint ubo) {
1402 if (!obj.deleted) {
1403 // Move the object outside the render bounds of the scene so it doesn't get rendered
1404 // TODO: Find a better way of hiding the object until the next time buffers are repopulated
1405 transformObject(obj, translate(mat4(1.0f), vec3(0.0f, 0.0f, FAR_CLIP * 1000.0f)), ubo);
1406 obj.deleted = true;
1407 }
1408}
1409
1410// TODO: Pass a reference, not a pointer
1411void calculateObjectBoundingBox(SceneObject* obj) {
1412 GLfloat min_x = obj->points[0];
1413 GLfloat max_x = obj->points[0];
1414 GLfloat min_y = obj->points[1];
1415 GLfloat max_y = obj->points[1];
1416 GLfloat min_z = obj->points[2];
1417 GLfloat max_z = obj->points[2];
1418
1419 // start from the second point
1420 for (unsigned int i = 3; i < obj->points.size(); i += 3) {
1421 if (min_x > obj->points[i]) {
1422 min_x = obj->points[i];
1423 }
1424 else if (max_x < obj->points[i]) {
1425 max_x = obj->points[i];
1426 }
1427
1428 if (min_y > obj->points[i + 1]) {
1429 min_y = obj->points[i + 1];
1430 }
1431 else if (max_y < obj->points[i + 1]) {
1432 max_y = obj->points[i + 1];
1433 }
1434
1435 if (min_z > obj->points[i + 2]) {
1436 min_z = obj->points[i + 2];
1437 }
1438 else if (max_z < obj->points[i + 2]) {
1439 max_z = obj->points[i + 2];
1440 }
1441 }
1442
1443 obj->bounding_center = vec3((min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f);
1444
1445 GLfloat radius_x = max_x - obj->bounding_center.x;
1446 GLfloat radius_y = max_y - obj->bounding_center.y;
1447 GLfloat radius_z = max_z - obj->bounding_center.z;
1448
1449 // TODO: This actually underestimates the radius. Might need to be fixed at some point.
1450 // TODO: Does not take into account any scaling in the model matrix
1451 obj->bounding_radius = radius_x;
1452 if (obj->bounding_radius < radius_y)
1453 obj->bounding_radius = radius_y;
1454 if (obj->bounding_radius < radius_z)
1455 obj->bounding_radius = radius_z;
1456
1457 for (unsigned int i = 0; i < obj->points.size(); i += 3) {
1458 obj->points[i] -= obj->bounding_center.x;
1459 obj->points[i + 1] -= obj->bounding_center.y;
1460 obj->points[i + 2] -= obj->bounding_center.z;
1461 }
1462
1463 obj->bounding_center = vec3(0.0f, 0.0f, 0.0f);
1464}
1465
1466SceneObject* createShip() {
1467 SceneObject* ship = new SceneObject();
1468
1469 ship->type = TYPE_SHIP;
1470
1471 ship->points = {
1472 //back
1473 -0.5f, 0.3f, 0.0f,
1474 -0.5f, 0.0f, 0.0f,
1475 0.5f, 0.0f, 0.0f,
1476 -0.5f, 0.3f, 0.0f,
1477 0.5f, 0.0f, 0.0f,
1478 0.5f, 0.3f, 0.0f,
1479
1480 // left back
1481 -0.5f, 0.3f, -2.0f,
1482 -0.5f, 0.0f, -2.0f,
1483 -0.5f, 0.0f, 0.0f,
1484 -0.5f, 0.3f, -2.0f,
1485 -0.5f, 0.0f, 0.0f,
1486 -0.5f, 0.3f, 0.0f,
1487
1488 // right back
1489 0.5f, 0.3f, 0.0f,
1490 0.5f, 0.0f, 0.0f,
1491 0.5f, 0.0f, -2.0f,
1492 0.5f, 0.3f, 0.0f,
1493 0.5f, 0.0f, -2.0f,
1494 0.5f, 0.3f, -2.0f,
1495
1496 // left mid
1497 -0.25f, 0.3f, -3.0f,
1498 -0.25f, 0.0f, -3.0f,
1499 -0.5f, 0.0f, -2.0f,
1500 -0.25f, 0.3f, -3.0f,
1501 -0.5f, 0.0f, -2.0f,
1502 -0.5f, 0.3f, -2.0f,
1503
1504 // right mid
1505 0.5f, 0.3f, -2.0f,
1506 0.5f, 0.0f, -2.0f,
1507 0.25f, 0.0f, -3.0f,
1508 0.5f, 0.3f, -2.0f,
1509 0.25f, 0.0f, -3.0f,
1510 0.25f, 0.3f, -3.0f,
1511
1512 // left front
1513 0.0f, 0.0f, -3.5f,
1514 -0.25f, 0.0f, -3.0f,
1515 -0.25f, 0.3f, -3.0f,
1516
1517 // right front
1518 0.25f, 0.3f, -3.0f,
1519 0.25f, 0.0f, -3.0f,
1520 0.0f, 0.0f, -3.5f,
1521
1522 // top back
1523 -0.5f, 0.3f, -2.0f,
1524 -0.5f, 0.3f, 0.0f,
1525 0.5f, 0.3f, 0.0f,
1526 -0.5f, 0.3f, -2.0f,
1527 0.5f, 0.3f, 0.0f,
1528 0.5f, 0.3f, -2.0f,
1529
1530 // bottom back
1531 -0.5f, 0.0f, 0.0f,
1532 -0.5f, 0.0f, -2.0f,
1533 0.5f, 0.0f, 0.0f,
1534 0.5f, 0.0f, 0.0f,
1535 -0.5f, 0.0f, -2.0f,
1536 0.5f, 0.0f, -2.0f,
1537
1538 // top mid
1539 -0.25f, 0.3f, -3.0f,
1540 -0.5f, 0.3f, -2.0f,
1541 0.5f, 0.3f, -2.0f,
1542 -0.25f, 0.3f, -3.0f,
1543 0.5f, 0.3f, -2.0f,
1544 0.25f, 0.3f, -3.0f,
1545
1546 // bottom mid
1547 -0.5f, 0.0f, -2.0f,
1548 -0.25f, 0.0f, -3.0f,
1549 0.5f, 0.0f, -2.0f,
1550 0.5f, 0.0f, -2.0f,
1551 -0.25f, 0.0f, -3.0f,
1552 0.25f, 0.0f, -3.0f,
1553
1554 // top front
1555 -0.25f, 0.3f, -3.0f,
1556 0.25f, 0.3f, -3.0f,
1557 0.0f, 0.0f, -3.5f,
1558
1559 // bottom front
1560 0.25f, 0.0f, -3.0f,
1561 -0.25f, 0.0f, -3.0f,
1562 0.0f, 0.0f, -3.5f,
1563
1564 // left wing start back
1565 -1.5f, 0.3f, 0.0f,
1566 -1.5f, 0.0f, 0.0f,
1567 -0.5f, 0.0f, 0.0f,
1568 -1.5f, 0.3f, 0.0f,
1569 -0.5f, 0.0f, 0.0f,
1570 -0.5f, 0.3f, 0.0f,
1571
1572 // left wing start top
1573 -0.5f, 0.3f, -0.3f,
1574 -1.3f, 0.3f, -0.3f,
1575 -1.5f, 0.3f, 0.0f,
1576 -0.5f, 0.3f, -0.3f,
1577 -1.5f, 0.3f, 0.0f,
1578 -0.5f, 0.3f, 0.0f,
1579
1580 // left wing start front
1581 -0.5f, 0.3f, -0.3f,
1582 -0.5f, 0.0f, -0.3f,
1583 -1.3f, 0.0f, -0.3f,
1584 -0.5f, 0.3f, -0.3f,
1585 -1.3f, 0.0f, -0.3f,
1586 -1.3f, 0.3f, -0.3f,
1587
1588 // left wing start bottom
1589 -0.5f, 0.0f, 0.0f,
1590 -1.5f, 0.0f, 0.0f,
1591 -1.3f, 0.0f, -0.3f,
1592 -0.5f, 0.0f, 0.0f,
1593 -1.3f, 0.0f, -0.3f,
1594 -0.5f, 0.0f, -0.3f,
1595
1596 // left wing end outside
1597 -1.5f, 0.3f, 0.0f,
1598 -2.2f, 0.15f, -0.8f,
1599 -1.5f, 0.0f, 0.0f,
1600
1601 // left wing end top
1602 -1.3f, 0.3f, -0.3f,
1603 -2.2f, 0.15f, -0.8f,
1604 -1.5f, 0.3f, 0.0f,
1605
1606 // left wing end front
1607 -1.3f, 0.0f, -0.3f,
1608 -2.2f, 0.15f, -0.8f,
1609 -1.3f, 0.3f, -0.3f,
1610
1611 // left wing end bottom
1612 -1.5f, 0.0f, 0.0f,
1613 -2.2f, 0.15f, -0.8f,
1614 -1.3f, 0.0f, -0.3f,
1615
1616 // right wing start back
1617 1.5f, 0.0f, 0.0f,
1618 1.5f, 0.3f, 0.0f,
1619 0.5f, 0.0f, 0.0f,
1620 0.5f, 0.0f, 0.0f,
1621 1.5f, 0.3f, 0.0f,
1622 0.5f, 0.3f, 0.0f,
1623
1624 // right wing start top
1625 1.3f, 0.3f, -0.3f,
1626 0.5f, 0.3f, -0.3f,
1627 1.5f, 0.3f, 0.0f,
1628 1.5f, 0.3f, 0.0f,
1629 0.5f, 0.3f, -0.3f,
1630 0.5f, 0.3f, 0.0f,
1631
1632 // right wing start front
1633 0.5f, 0.0f, -0.3f,
1634 0.5f, 0.3f, -0.3f,
1635 1.3f, 0.0f, -0.3f,
1636 1.3f, 0.0f, -0.3f,
1637 0.5f, 0.3f, -0.3f,
1638 1.3f, 0.3f, -0.3f,
1639
1640 // right wing start bottom
1641 1.5f, 0.0f, 0.0f,
1642 0.5f, 0.0f, 0.0f,
1643 1.3f, 0.0f, -0.3f,
1644 1.3f, 0.0f, -0.3f,
1645 0.5f, 0.0f, 0.0f,
1646 0.5f, 0.0f, -0.3f,
1647
1648 // right wing end outside
1649 2.2f, 0.15f, -0.8f,
1650 1.5f, 0.3f, 0.0f,
1651 1.5f, 0.0f, 0.0f,
1652
1653 // right wing end top
1654 2.2f, 0.15f, -0.8f,
1655 1.3f, 0.3f, -0.3f,
1656 1.5f, 0.3f, 0.0f,
1657
1658 // right wing end front
1659 2.2f, 0.15f, -0.8f,
1660 1.3f, 0.0f, -0.3f,
1661 1.3f, 0.3f, -0.3f,
1662
1663 // right wing end bottom
1664 2.2f, 0.15f, -0.8f,
1665 1.5f, 0.0f, 0.0f,
1666 1.3f, 0.0f, -0.3f,
1667 };
1668 ship->colors = {
1669 0.0f, 0.0f, 0.3f,
1670 0.0f, 0.0f, 0.3f,
1671 0.0f, 0.0f, 0.3f,
1672 0.0f, 0.0f, 0.3f,
1673 0.0f, 0.0f, 0.3f,
1674 0.0f, 0.0f, 0.3f,
1675
1676 0.0f, 0.0f, 0.3f,
1677 0.0f, 0.0f, 0.3f,
1678 0.0f, 0.0f, 0.3f,
1679 0.0f, 0.0f, 0.3f,
1680 0.0f, 0.0f, 0.3f,
1681 0.0f, 0.0f, 0.3f,
1682
1683 0.0f, 0.0f, 0.3f,
1684 0.0f, 0.0f, 0.3f,
1685 0.0f, 0.0f, 0.3f,
1686 0.0f, 0.0f, 0.3f,
1687 0.0f, 0.0f, 0.3f,
1688 0.0f, 0.0f, 0.3f,
1689
1690 0.0f, 0.0f, 0.3f,
1691 0.0f, 0.0f, 0.3f,
1692 0.0f, 0.0f, 0.3f,
1693 0.0f, 0.0f, 0.3f,
1694 0.0f, 0.0f, 0.3f,
1695 0.0f, 0.0f, 0.3f,
1696
1697 0.0f, 0.0f, 0.3f,
1698 0.0f, 0.0f, 0.3f,
1699 0.0f, 0.0f, 0.3f,
1700 0.0f, 0.0f, 0.3f,
1701 0.0f, 0.0f, 0.3f,
1702 0.0f, 0.0f, 0.3f,
1703
1704 0.0f, 0.0f, 1.0f,
1705 0.0f, 0.0f, 1.0f,
1706 0.0f, 0.0f, 1.0f,
1707
1708 0.0f, 0.0f, 1.0f,
1709 0.0f, 0.0f, 1.0f,
1710 0.0f, 0.0f, 1.0f,
1711
1712 0.0f, 0.0f, 1.0f,
1713 0.0f, 0.0f, 1.0f,
1714 0.0f, 0.0f, 1.0f,
1715 0.0f, 0.0f, 1.0f,
1716 0.0f, 0.0f, 1.0f,
1717 0.0f, 0.0f, 1.0f,
1718
1719 0.0f, 0.0f, 1.0f,
1720 0.0f, 0.0f, 1.0f,
1721 0.0f, 0.0f, 1.0f,
1722 0.0f, 0.0f, 1.0f,
1723 0.0f, 0.0f, 1.0f,
1724 0.0f, 0.0f, 1.0f,
1725
1726 0.0f, 0.0f, 1.0f,
1727 0.0f, 0.0f, 1.0f,
1728 0.0f, 0.0f, 1.0f,
1729 0.0f, 0.0f, 1.0f,
1730 0.0f, 0.0f, 1.0f,
1731 0.0f, 0.0f, 1.0f,
1732
1733 0.0f, 0.0f, 1.0f,
1734 0.0f, 0.0f, 1.0f,
1735 0.0f, 0.0f, 1.0f,
1736 0.0f, 0.0f, 1.0f,
1737 0.0f, 0.0f, 1.0f,
1738 0.0f, 0.0f, 1.0f,
1739
1740 0.0f, 0.0f, 0.3f,
1741 0.0f, 0.0f, 0.3f,
1742 0.0f, 0.0f, 0.3f,
1743
1744 0.0f, 0.0f, 0.3f,
1745 0.0f, 0.0f, 0.3f,
1746 0.0f, 0.0f, 0.3f,
1747
1748 0.0f, 0.0f, 0.3f,
1749 0.0f, 0.0f, 0.3f,
1750 0.0f, 0.0f, 0.3f,
1751 0.0f, 0.0f, 0.3f,
1752 0.0f, 0.0f, 0.3f,
1753 0.0f, 0.0f, 0.3f,
1754
1755 0.0f, 0.0f, 0.3f,
1756 0.0f, 0.0f, 0.3f,
1757 0.0f, 0.0f, 0.3f,
1758 0.0f, 0.0f, 0.3f,
1759 0.0f, 0.0f, 0.3f,
1760 0.0f, 0.0f, 0.3f,
1761
1762 0.0f, 0.0f, 0.3f,
1763 0.0f, 0.0f, 0.3f,
1764 0.0f, 0.0f, 0.3f,
1765 0.0f, 0.0f, 0.3f,
1766 0.0f, 0.0f, 0.3f,
1767 0.0f, 0.0f, 0.3f,
1768
1769 0.0f, 0.0f, 0.3f,
1770 0.0f, 0.0f, 0.3f,
1771 0.0f, 0.0f, 0.3f,
1772 0.0f, 0.0f, 0.3f,
1773 0.0f, 0.0f, 0.3f,
1774 0.0f, 0.0f, 0.3f,
1775
1776 0.0f, 0.0f, 0.3f,
1777 0.0f, 0.0f, 0.3f,
1778 0.0f, 0.0f, 0.3f,
1779
1780 0.0f, 0.0f, 0.3f,
1781 0.0f, 0.0f, 0.3f,
1782 0.0f, 0.0f, 0.3f,
1783
1784 0.0f, 0.0f, 0.3f,
1785 0.0f, 0.0f, 0.3f,
1786 0.0f, 0.0f, 0.3f,
1787
1788 0.0f, 0.0f, 0.3f,
1789 0.0f, 0.0f, 0.3f,
1790 0.0f, 0.0f, 0.3f,
1791
1792 0.0f, 0.0f, 0.3f,
1793 0.0f, 0.0f, 0.3f,
1794 0.0f, 0.0f, 0.3f,
1795 0.0f, 0.0f, 0.3f,
1796 0.0f, 0.0f, 0.3f,
1797 0.0f, 0.0f, 0.3f,
1798
1799 0.0f, 0.0f, 0.3f,
1800 0.0f, 0.0f, 0.3f,
1801 0.0f, 0.0f, 0.3f,
1802 0.0f, 0.0f, 0.3f,
1803 0.0f, 0.0f, 0.3f,
1804 0.0f, 0.0f, 0.3f,
1805
1806 0.0f, 0.0f, 0.3f,
1807 0.0f, 0.0f, 0.3f,
1808 0.0f, 0.0f, 0.3f,
1809 0.0f, 0.0f, 0.3f,
1810 0.0f, 0.0f, 0.3f,
1811 0.0f, 0.0f, 0.3f,
1812
1813 0.0f, 0.0f, 0.3f,
1814 0.0f, 0.0f, 0.3f,
1815 0.0f, 0.0f, 0.3f,
1816 0.0f, 0.0f, 0.3f,
1817 0.0f, 0.0f, 0.3f,
1818 0.0f, 0.0f, 0.3f,
1819
1820 0.0f, 0.0f, 0.3f,
1821 0.0f, 0.0f, 0.3f,
1822 0.0f, 0.0f, 0.3f,
1823
1824 0.0f, 0.0f, 0.3f,
1825 0.0f, 0.0f, 0.3f,
1826 0.0f, 0.0f, 0.3f,
1827
1828 0.0f, 0.0f, 0.3f,
1829 0.0f, 0.0f, 0.3f,
1830 0.0f, 0.0f, 0.3f,
1831
1832 0.0f, 0.0f, 0.3f,
1833 0.0f, 0.0f, 0.3f,
1834 0.0f, 0.0f, 0.3f,
1835 };
1836 ship->texcoords = { 0.0f };
1837
1838 mat4 T_model = translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f));
1839 mat4 R_model(1.0f);
1840 ship->model_base = T_model * R_model * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
1841
1842 ship->translate_mat = T_model;
1843
1844 initObject(ship);
1845
1846 return ship;
1847}
1848
1849/* LASER RENDERING/POSITIONING ALGORITHM
1850 * -Draw a thin rectangle for the laser beam, using the specified width and endpoints
1851 * -Texture the beam with a grayscale partially transparent image
1852 * -In the shader, blend the image with a color to support lasers of different colors
1853 *
1854 * The flat part of the textured rectangle needs to always face the camera, so the laser's width is constant
1855 * This is done as follows:
1856* -Determine the length of the laser based on the start and end points
1857* -Draw a rectangle along the z-axis and rotated upwards along the y-axis, with the correct final length and width
1858* -Rotate the beam around the z-axis by the correct angle, sot that in its final position, the flat part faces the camera
1859* -Rotate the beam along the x-axis and then along the y-axis and then translate it to put it into its final position
1860*/
1861// TODO: Make the color parameter have an effect
1862Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width) {
1863 Laser* obj = new Laser();
1864 obj->type = TYPE_LASER;
1865 obj->targetAsteroid = NULL;
1866
1867 vec3 ray = end - start;
1868 float length = glm::length(ray);
1869
1870 obj->points = {
1871 width / 2, 0.0f, -width / 2,
1872 -width / 2, 0.0f, -width / 2,
1873 -width / 2, 0.0f, 0.0f,
1874 width / 2, 0.0f, -width / 2,
1875 -width / 2, 0.0f, 0.0f,
1876 width / 2, 0.0f, 0.0f,
1877 width / 2, 0.0f, -length + width / 2,
1878 -width / 2, 0.0f, -length + width / 2,
1879 -width / 2, 0.0f, -width / 2,
1880 width / 2, 0.0f, -length + width / 2,
1881 -width / 2, 0.0f, -width / 2,
1882 width / 2, 0.0f, -width / 2,
1883 width / 2, 0.0f, -length,
1884 -width / 2, 0.0f, -length,
1885 -width / 2, 0.0f, -length + width / 2,
1886 width / 2, 0.0f, -length,
1887 -width / 2, 0.0f, -length + width / 2,
1888 width / 2, 0.0f, -length + width / 2,
1889 };
1890
1891 obj->texcoords = {
1892 1.0f, 0.5f,
1893 0.0f, 0.5f,
1894 0.0f, 0.0f,
1895 1.0f, 0.5f,
1896 0.0f, 0.0f,
1897 1.0f, 0.0f,
1898 1.0f, 0.51f,
1899 0.0f, 0.51f,
1900 0.0f, 0.49f,
1901 1.0f, 0.51f,
1902 0.0f, 0.49f,
1903 1.0f, 0.49f,
1904 1.0f, 1.0f,
1905 0.0f, 1.0f,
1906 0.0f, 0.5f,
1907 1.0f, 1.0f,
1908 0.0f, 0.5f,
1909 1.0f, 0.5f,
1910 };
1911
1912 float xAxisRotation = asin(ray.y / length);
1913 float yAxisRotation = atan2(-ray.x, -ray.z);
1914
1915 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1916 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1917 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1918
1919 // To project point P onto line AB:
1920 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1921 vec3 projOnLaser = start + glm::dot(cam_pos-start, ray) / (length*length) * ray;
1922 vec3 laserToCam = cam_pos - projOnLaser;
1923
1924 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1925
1926 obj->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1927
1928 initObject(obj);
1929
1930 obj->model_transform = rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) * obj->model_transform;
1931 obj->model_transform = rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) * obj->model_transform;
1932 obj->model_transform = translate(mat4(1.0f), start) * obj->model_transform;
1933
1934 return obj;
1935}
1936
1937ShaderModelGroup createModelGroup(GLuint shaderProgram) {
1938 ShaderModelGroup smg;
1939
1940 smg.shaderProgram = shaderProgram;
1941 glGenVertexArrays(1, &smg.vao);
1942 smg.numPoints = 0;
1943
1944 return smg;
1945}
1946
1947// TODO: Add the code to resize the buffers here
1948// addObjectToScene and removeObjectFromScene pretty much already do this.
1949// However, when addObjectToScene resizes the buffers, it resizes them for all object types
1950// It would be more efficient to only resize them for the object type in question
1951
1952void removeModelFromGroup(ShaderModelGroup& modelGroup, SceneObject& model) {
1953 // TODO: Implement
1954}
1955
1956void addModelToGroup(ShaderModelGroup& modelGroup, SceneObject& model) {
1957 // TODO: Implement
1958}
1959
1960void defineModelGroupAttrib(ShaderModelGroup& modelGroup, string name, AttribType attribType,
1961 GLint size, GLenum type, size_t fieldOffset) {
1962 if (type != GL_FLOAT && type != GL_UNSIGNED_INT) {
1963 cout << "Unknown shader program attribute type: " << type << endl;
1964 return;
1965 }
1966
1967 AttribInfo attribInfo;
1968
1969 attribInfo.attribType = attribType;
1970 attribInfo.index = modelGroup.attribs.size();
1971 attribInfo.size = size;
1972 attribInfo.type = type;
1973 attribInfo.fieldOffset = fieldOffset;
1974
1975 modelGroup.attribs[name] = attribInfo;
1976}
1977
1978void defineModelGroupUniform(ShaderModelGroup& modelGroup, string name, AttribType attribType,
1979 GLint size, UniformType type, GLfloat* data) {
1980 AttribInfo attribInfo;
1981
1982 attribInfo.attribType = attribType;
1983 attribInfo.size = size;
1984 attribInfo.uniType = type;
1985 attribInfo.data = data;
1986
1987 modelGroup.attribs[name] = attribInfo;
1988}
1989
1990void initModelGroupAttribs(ShaderModelGroup& modelGroup) {
1991 glBindVertexArray(modelGroup.vao);
1992
1993 map<string, AttribInfo>::iterator it;
1994 for (it = modelGroup.attribs.begin(); it != modelGroup.attribs.end(); it++) {
1995 if (it->second.attribType == ATTRIB_UNIFORM) {
1996 it->second.buffer = glGetUniformLocation(modelGroup.shaderProgram, it->first.c_str());
1997 } else {
1998 glEnableVertexAttribArray(it->second.index);
1999
2000 glGenBuffers(1, &it->second.buffer);
2001 glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
2002
2003 switch (it->second.type) {
2004 case GL_FLOAT: {
2005 glVertexAttribPointer(it->second.index, it->second.size, it->second.type, GL_FALSE, 0, NULL);
2006 break;
2007 }
2008 case GL_UNSIGNED_INT: {
2009 glVertexAttribIPointer(it->second.index, it->second.size, it->second.type, 0, NULL);
2010 break;
2011 }
2012 }
2013 }
2014 }
2015}
2016
2017void bindUniformData(AttribInfo& attrib) {
2018 switch(attrib.uniType) {
2019 case UNIFORM_MATRIX_4F:
2020 glUniformMatrix4fv(attrib.buffer, attrib.size, GL_FALSE, attrib.data);
2021 break;
2022 case UNIFORM_1F:
2023 glUniform1fv(attrib.buffer, attrib.size, attrib.data);
2024 break;
2025 case UNIFORM_3F:
2026 glUniform3fv(attrib.buffer, attrib.size, attrib.data);
2027 break;
2028 case UNIFORM_NONE:
2029 break;
2030 }
2031}
2032
2033void bindUniformData(AttribInfo& attrib, GLfloat *data) {
2034 switch(attrib.uniType) {
2035 case UNIFORM_MATRIX_4F:
2036 glUniformMatrix4fv(attrib.buffer, attrib.size, GL_FALSE, data);
2037 break;
2038 case UNIFORM_1F:
2039 glUniform1fv(attrib.buffer, attrib.size, data);
2040 break;
2041 case UNIFORM_3F:
2042 glUniform3fv(attrib.buffer, attrib.size, data);
2043 break;
2044 case UNIFORM_NONE:
2045 break;
2046 }
2047}
2048
2049/* The purpose of this function is to replace the use of sizeof() when calling
2050 * function like glBufferSubData and using AttribInfo to get offsets and types
2051 * I need instead of hardcoding them. I can't save a type like GLfloat, but I cam
2052 * save GL_FLOAT and use this function to return sizeof(GLfloat) when GL_FLOAT is
2053 * passed.
2054 */
2055size_t GLsizeof(GLenum type) {
2056 switch (type) {
2057 case GL_FLOAT:
2058 return sizeof(GLfloat);
2059 case GL_UNSIGNED_INT:
2060 return sizeof(GLuint);
2061 default:
2062 cout << "Uknown GL type passed to GLsizeof: " << type << endl;
2063 return 0;
2064 }
2065}
2066
2067/* This function returns a reference to the first element of a given vector
2068 * attribute in obj. The vector is assumed to hold GLfloats. If the same thing
2069 * needs to be done later for vectors of other types, we could pass in a GLenum,
2070 * and do something similar to GLsizeof
2071 */
2072GLvoid* getVectorAttribPtr(SceneObject& obj, size_t attribOffset) {
2073 return (GLvoid*)(&(*(vector<GLfloat>*)((size_t)&obj + attribOffset))[0]);
2074}
2075
2076GLvoid* getScalarAttribPtr(SceneObject& obj, size_t attribOffset) {
2077 return (GLvoid*)((size_t)&obj + attribOffset);
2078}
2079
2080void populateBuffers(vector<SceneObject*>& objects,
2081 map<GLuint, BufferInfo>& shaderBufferInfo,
2082 map<ObjectType, ShaderModelGroup>& modelGroups,
2083 GLuint ubo) {
2084 GLsizeiptr num_points = 0;
2085 GLsizeiptr num_objects = 0;
2086
2087 map<GLuint, unsigned int> shaderCounts;
2088 map<GLuint, unsigned int> shaderUboCounts;
2089
2090 map<GLuint, BufferInfo>::iterator shaderIt;
2091
2092 for (shaderIt = shaderBufferInfo.begin(); shaderIt != shaderBufferInfo.end(); shaderIt++) {
2093 shaderCounts[shaderIt->first] = 0;
2094 shaderUboCounts[shaderIt->first] = 0;
2095 }
2096
2097 vector<SceneObject*>::iterator it;
2098
2099 /* Find all shaders that need to be used and the number of objects and
2100 * number of points for each shader. Construct a map from shader id to count
2101 * of points being drawn using that shader (for thw model matrix ubo, we
2102 * need object counts instead). These will be used to get offsets into the
2103 * vertex buffer for each shader.
2104 */
2105 for (it = objects.begin(); it != objects.end(); ) {
2106 if ((*it)->deleted) {
2107 delete *it;
2108 it = objects.erase(it);
2109 } else {
2110 num_points += (*it)->num_points;
2111 num_objects++;
2112
2113 shaderCounts[modelGroups[(*it)->type].shaderProgram] += (*it)->num_points;
2114 shaderUboCounts[modelGroups[(*it)->type].shaderProgram]++;
2115
2116 it++;
2117 }
2118 }
2119
2120 // double the buffer sizes to leave room for new objects
2121 num_points *= 2;
2122 num_objects *= 2;
2123
2124 map<GLuint, unsigned int>::iterator shaderCountIt;
2125 unsigned int lastShaderCount = 0;
2126 unsigned int lastShaderUboCount = 0;
2127
2128 /*
2129 * The counts calculated above can be used to get the starting offset of
2130 * each shader in the vertex buffer. Create a map of base offsets to mark
2131 * where the data for the first object using a given shader begins. Also,
2132 * create a map of current offsets to mark where to copy data for the next
2133 * object being added.
2134 */
2135 for (shaderCountIt = shaderCounts.begin(); shaderCountIt != shaderCounts.end(); shaderCountIt++) {
2136 // When populating the buffers, leave as much empty space as space taken up by existing objects
2137 // to allow new objects to be added without immediately having to resize the buffers
2138 shaderBufferInfo[shaderCountIt->first].ubo_base = lastShaderUboCount * 2;
2139
2140 shaderBufferInfo[shaderCountIt->first].ubo_offset = 0;
2141
2142 shaderBufferInfo[shaderCountIt->first].ubo_capacity = shaderUboCounts[shaderCountIt->first] * 2;
2143
2144 lastShaderCount += shaderCounts[shaderCountIt->first];
2145 lastShaderUboCount += shaderUboCounts[shaderCountIt->first];
2146 }
2147
2148 map<ObjectType, ShaderModelGroup>::iterator modelGroupIt;
2149 ShaderModelGroup* smg;
2150 for (modelGroupIt = modelGroups.begin(); modelGroupIt != modelGroups.end(); modelGroupIt++) {
2151 smg = &modelGroups[modelGroupIt->first];
2152
2153 smg->numPoints = 0;
2154 smg->vboCapacity = shaderCounts[smg->shaderProgram] * 2;
2155
2156 map<string, AttribInfo>::iterator attrIt;
2157 for (attrIt = smg->attribs.begin(); attrIt != smg->attribs.end(); attrIt++) {
2158 if (attrIt->second.attribType != ATTRIB_UNIFORM) {
2159 glBindBuffer(GL_ARRAY_BUFFER, attrIt->second.buffer);
2160 glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrIt->second.type) * attrIt->second.size, NULL, GL_DYNAMIC_DRAW);
2161 }
2162 }
2163 }
2164
2165 // Allocate the ubo using the counts calculated above
2166
2167 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2168 glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
2169
2170 for (it = objects.begin(); it != objects.end(); it++) {
2171 copyObjectDataToBuffers(**it, shaderBufferInfo, modelGroups, ubo);
2172 }
2173}
2174
2175void copyObjectDataToBuffers(SceneObject& obj,
2176 map<GLuint, BufferInfo>& shaderBufferInfo,
2177 map<ObjectType, ShaderModelGroup>& modelGroups,
2178 GLuint ubo) {
2179 BufferInfo* bufferInfo = &shaderBufferInfo[modelGroups[obj.type].shaderProgram];
2180
2181 obj.vertex_vbo_offset = modelGroups[obj.type].numPoints;
2182 obj.ubo_offset = bufferInfo->ubo_base + bufferInfo->ubo_offset;
2183
2184 ShaderModelGroup& smg = modelGroups[obj.type];
2185
2186 for (map<string, AttribInfo>::iterator it = smg.attribs.begin(); it != smg.attribs.end(); it++) {
2187 glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
2188
2189 switch (it->second.attribType) {
2190 case ATTRIB_POINT_VARYING:
2191 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * GLsizeof(it->second.type) * it->second.size,
2192 obj.num_points * GLsizeof(it->second.type) * it->second.size, getVectorAttribPtr(obj, it->second.fieldOffset));
2193 break;
2194 case ATTRIB_OBJECT_VARYING:
2195 for (unsigned int i = 0; i < obj.num_points; i++) {
2196 glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * GLsizeof(it->second.type) * it->second.size,
2197 GLsizeof(it->second.type) * it->second.size, getScalarAttribPtr(obj, it->second.fieldOffset));
2198 }
2199 break;
2200 case ATTRIB_UNIFORM:
2201 break;
2202 }
2203 }
2204
2205 obj.model_mat = obj.model_transform * obj.model_base;
2206
2207 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2208 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2209
2210 if (obj.type == TYPE_ASTEROID) {
2211 glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
2212
2213 ostringstream oss;
2214 oss << "hp[" << obj.ubo_offset << "]";
2215 glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid&)obj).hp);
2216 } else if (obj.type == TYPE_EXPLOSION) {
2217 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
2218
2219 ostringstream oss;
2220 oss << "explosion_start_time[" << obj.ubo_offset << "]";
2221 glUniform1f(glGetUniformLocation(modelGroups[TYPE_EXPLOSION].shaderProgram, oss.str().c_str()), ((ParticleEffect&)obj).startTime);
2222 }
2223
2224 modelGroups[obj.type].numPoints += obj.num_points;
2225 bufferInfo->ubo_offset++;
2226}
2227
2228void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo) {
2229 if (obj.deleted) return;
2230
2231 obj.model_transform = transform * obj.model_transform;
2232 obj.model_mat = obj.model_transform * obj.model_base;
2233
2234 obj.bounding_center = vec3(transform * vec4(obj.bounding_center, 1.0f));
2235
2236 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2237 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2238}
2239
2240void translateLaser(Laser* laser, const vec3& translation, GLuint ubo) {
2241 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2242 // and then re-used here
2243
2244 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2245 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[38], 1.0f));
2246
2247 vec3 ray = end - start;
2248 float length = glm::length(ray);
2249
2250 float xAxisRotation = asin(ray.y / length);
2251 float yAxisRotation = atan2(-ray.x, -ray.z);
2252
2253 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2254 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2255 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2256
2257 // To project point P onto line AB:
2258 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2259 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
2260 vec3 laserToCam = cam_pos - projOnLaser;
2261
2262 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2263
2264 laser->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2265
2266 transformObject(*laser, translate(mat4(1.0f), translation), ubo);
2267}
2268
2269void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, ShaderModelGroup& laserSmg, GLuint asteroid_sp) {
2270 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2271 // and then re-used here
2272
2273 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2274 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[2] + laser->points[20], 1.0f));
2275
2276 vec3 intersection(0.0f), closestIntersection(0.0f);
2277 Asteroid* closestAsteroid = NULL;
2278
2279 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
2280 if ((*it)->type == TYPE_ASTEROID && !(*it)->deleted && getLaserAndAsteroidIntersection(start, end, **it, intersection)) {
2281 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2282 if (closestAsteroid == NULL || intersection.z > closestIntersection.z) {
2283 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2284 closestAsteroid = (Asteroid*)*it;
2285 closestIntersection = intersection;
2286 }
2287 }
2288 }
2289
2290 float width = laser->points[0] - laser->points[2];
2291
2292 if (laser->targetAsteroid != closestAsteroid) {
2293 if (laser->targetAsteroid != NULL) {
2294 if (laser == leftLaser) {
2295 leftLaserEffect->deleted = true;
2296 } else if (laser == rightLaser) {
2297 rightLaserEffect->deleted = true;
2298 }
2299 }
2300
2301 EffectOverTime* eot = NULL;
2302
2303 if (closestAsteroid != NULL) {
2304 eot = new EffectOverTime(closestAsteroid->hp, -20.0f, closestAsteroid);
2305 effects.push_back(eot);
2306 }
2307
2308 if (laser == leftLaser) {
2309 leftLaserEffect = eot;
2310 } else if (laser == rightLaser) {
2311 rightLaserEffect = eot;
2312 }
2313 }
2314 laser->targetAsteroid = closestAsteroid;
2315
2316 float length = 5.24f; // I think this was to make sure the laser went past the end of the screen
2317 if (closestAsteroid != NULL) {
2318 length = glm::length(closestIntersection - start);
2319
2320 // TODO: Find a more generic way of updating the asteroid hp than in updateLaserTarget
2321
2322 glUseProgram(asteroid_sp);
2323
2324 ostringstream oss;
2325 oss << "hp[" << closestAsteroid->ubo_offset << "]";
2326 glUniform1f(glGetUniformLocation(asteroid_sp, oss.str().c_str()), closestAsteroid->hp);
2327 }
2328
2329 laser->points[20] = -length + width / 2;
2330 laser->points[23] = -length + width / 2;
2331 laser->points[29] = -length + width / 2;
2332 laser->points[38] = -length;
2333 laser->points[41] = -length;
2334 laser->points[44] = -length + width / 2;
2335 laser->points[47] = -length;
2336 laser->points[50] = -length + width / 2;
2337 laser->points[53] = -length + width / 2;
2338
2339 AttribInfo* attrib = &laserSmg.attribs["vertex_position"];
2340 glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
2341 glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * GLsizeof(attrib->type) * attrib->size,
2342 laser->num_points * GLsizeof(attrib->type) * attrib->size, getVectorAttribPtr(*laser, attrib->fieldOffset));
2343}
2344
2345bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection) {
2346 /*
2347 ### LINE EQUATIONS ###
2348 x = x1 + u * (x2 - x1)
2349 y = y1 + u * (y2 - y1)
2350 z = z1 + u * (z2 - z1)
2351
2352 ### SPHERE EQUATION ###
2353 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2354
2355 ### QUADRATIC EQUATION TO SOLVE ###
2356 a*u^2 + b*u + c = 0
2357 WHERE THE CONSTANTS ARE
2358 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2359 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2360 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2361
2362 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2363
2364 If the value under the root is >= 0, we got an intersection
2365 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2366 one closer to the laser start point
2367 */
2368
2369 vec3& center = asteroid.bounding_center;
2370
2371 float a = pow(end.x-start.x, 2) + pow(end.y-start.y, 2) + pow(end.z-start.z, 2);
2372 float b = 2*((start.x-end.x)*(start.x-center.x) + (end.y-start.y)*(start.y-center.y) + (end.z-start.z)*(start.z-center.z));
2373 float c = pow(center.x, 2) + pow(center.y, 2) + pow(center.z, 2) + pow(start.x, 2) + pow(start.y, 2) + pow(start.z, 2) - 2*(center.x*start.x + center.y*start.y + center.z*start.z) - pow(asteroid.bounding_radius, 2);
2374 float discriminant = pow(b, 2) - 4*a*c;
2375
2376 if (discriminant >= 0.0f) {
2377 // In this case, the negative root will always give the point closer to the laser start point
2378 float u = (-b - sqrt(discriminant)) / (2 * a);
2379
2380 // Check that the intersection is within the line segment corresponding to the laser
2381 if (0.0f <= u && u <= 1.0f) {
2382 intersection = start + u * (end - start);
2383 return true;
2384 }
2385 }
2386
2387 return false;
2388}
2389
2390void renderScene(map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo) {
2391
2392 glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
2393 glBindVertexArray(modelGroups[TYPE_SHIP].vao);
2394
2395 glDrawArrays(GL_TRIANGLES, 0, modelGroups[TYPE_SHIP].numPoints);
2396
2397 glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
2398 glBindVertexArray(modelGroups[TYPE_ASTEROID].vao);
2399
2400 glDrawArrays(GL_TRIANGLES, 0, modelGroups[TYPE_ASTEROID].numPoints);
2401
2402 glEnable(GL_BLEND);
2403
2404 glUseProgram(modelGroups[TYPE_LASER].shaderProgram);
2405 glBindVertexArray(modelGroups[TYPE_LASER].vao);
2406
2407 glDrawArrays(GL_TRIANGLES, 0, modelGroups[TYPE_LASER].numPoints);
2408
2409 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
2410 glBindVertexArray(modelGroups[TYPE_EXPLOSION].vao);
2411
2412 glEnable(GL_PROGRAM_POINT_SIZE);
2413
2414 glDrawArrays(GL_POINTS, 0, modelGroups[TYPE_EXPLOSION].numPoints);
2415
2416 glDisable(GL_PROGRAM_POINT_SIZE);
2417 glDisable(GL_BLEND);
2418}
2419
2420void renderSceneGui(map<string, vector<UIValue>> valueLists) {
2421 ImGui_ImplGlfwGL3_NewFrame();
2422
2423 // 1. Show a simple window.
2424 // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
2425 /*
2426 {
2427 static float f = 0.0f;
2428 static int counter = 0;
2429 ImGui::Text("Hello, world!"); // Display some text (you can use a format string too)
2430 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
2431 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
2432
2433 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state
2434 ImGui::Checkbox("Another Window", &show_another_window);
2435
2436 if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated)
2437 counter++;
2438 ImGui::SameLine();
2439 ImGui::Text("counter = %d", counter);
2440
2441 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
2442 }
2443 */
2444
2445 {
2446 ImGui::SetNextWindowSize(ImVec2(95, 46), ImGuiCond_Once);
2447 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
2448 ImGui::Begin("WndStats", NULL,
2449 ImGuiWindowFlags_NoTitleBar |
2450 ImGuiWindowFlags_NoResize |
2451 ImGuiWindowFlags_NoMove);
2452
2453 renderGuiValueList(valueLists["stats value list"]);
2454
2455 ImGui::End();
2456 }
2457
2458 {
2459 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
2460 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
2461 ImGui::Begin("WndMenubar", NULL,
2462 ImGuiWindowFlags_NoTitleBar |
2463 ImGuiWindowFlags_NoResize |
2464 ImGuiWindowFlags_NoMove);
2465 ImGui::InvisibleButton("", ImVec2(155, 18));
2466 ImGui::SameLine();
2467 if (ImGui::Button("Main Menu")) {
2468 events.push(EVENT_GO_TO_MAIN_MENU);
2469 }
2470 ImGui::End();
2471 }
2472
2473 {
2474 ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
2475 ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
2476 ImGui::Begin("WndDebug", NULL,
2477 ImGuiWindowFlags_NoTitleBar |
2478 ImGuiWindowFlags_NoResize |
2479 ImGuiWindowFlags_NoMove);
2480
2481 renderGuiValueList(valueLists["debug value list"]);
2482
2483 ImGui::End();
2484 }
2485
2486 ImGui::Render();
2487 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2488}
2489
2490void renderMainMenu() {
2491}
2492
2493void renderMainMenuGui() {
2494 ImGui_ImplGlfwGL3_NewFrame();
2495
2496 {
2497 int padding = 4;
2498 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
2499 ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
2500 ImGui::Begin("WndMain", NULL,
2501 ImGuiWindowFlags_NoTitleBar |
2502 ImGuiWindowFlags_NoResize |
2503 ImGuiWindowFlags_NoMove);
2504
2505 ImGui::InvisibleButton("", ImVec2(10, 80));
2506 ImGui::InvisibleButton("", ImVec2(285, 18));
2507 ImGui::SameLine();
2508 if (ImGui::Button("New Game")) {
2509 events.push(EVENT_GO_TO_GAME);
2510 }
2511
2512 ImGui::InvisibleButton("", ImVec2(10, 15));
2513 ImGui::InvisibleButton("", ImVec2(300, 18));
2514 ImGui::SameLine();
2515 if (ImGui::Button("Quit")) {
2516 events.push(EVENT_QUIT);
2517 }
2518
2519 ImGui::End();
2520 }
2521
2522 ImGui::Render();
2523 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2524}
2525
2526void initGuiValueLists(map<string, vector<UIValue>> valueLists) {
2527 valueLists["stats value list"] = vector<UIValue>();
2528 valueLists["debug value list"] = vector<UIValue>();
2529}
2530
2531void renderGuiValueList(vector<UIValue>& values) {
2532 float maxWidth = 0.0f;
2533 float cursorStartPos = ImGui::GetCursorPosX();
2534
2535 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2536 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2537
2538 if (maxWidth < textWidth)
2539 maxWidth = textWidth;
2540 }
2541
2542 stringstream ss;
2543
2544 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2545 ss.str("");
2546 ss.clear();
2547
2548 switch (it->type) {
2549 case UIVALUE_INT:
2550 ss << it->label << ": " << *(unsigned int*)it->value;
2551 break;
2552 case UIVALUE_DOUBLE:
2553 ss << it->label << ": " << *(double*)it->value;
2554 break;
2555 }
2556
2557 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2558
2559 ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
2560 ImGui::Text("%s", ss.str().c_str());
2561 }
2562}
2563
2564Asteroid* createAsteroid(vec3 pos) {
2565 Asteroid* obj = new Asteroid();
2566 obj->type = TYPE_ASTEROID;
2567 obj->hp = 10.0f;
2568
2569 obj->points = {
2570 // front
2571 1.0f, 1.0f, 1.0f,
2572 -1.0f, 1.0f, 1.0f,
2573 -1.0f, -1.0f, 1.0f,
2574 1.0f, 1.0f, 1.0f,
2575 -1.0f, -1.0f, 1.0f,
2576 1.0f, -1.0f, 1.0f,
2577
2578 // top
2579 1.0f, 1.0f, -1.0f,
2580 -1.0f, 1.0f, -1.0f,
2581 -1.0f, 1.0f, 1.0f,
2582 1.0f, 1.0f, -1.0f,
2583 -1.0f, 1.0f, 1.0f,
2584 1.0f, 1.0f, 1.0f,
2585
2586 // bottom
2587 1.0f, -1.0f, 1.0f,
2588 -1.0f, -1.0f, 1.0f,
2589 -1.0f, -1.0f, -1.0f,
2590 1.0f, -1.0f, 1.0f,
2591 -1.0f, -1.0f, -1.0f,
2592 1.0f, -1.0f, -1.0f,
2593
2594 // back
2595 1.0f, 1.0f, -1.0f,
2596 -1.0f, -1.0f, -1.0f,
2597 -1.0f, 1.0f, -1.0f,
2598 1.0f, 1.0f, -1.0f,
2599 1.0f, -1.0f, -1.0f,
2600 -1.0f, -1.0f, -1.0f,
2601
2602 // right
2603 1.0f, 1.0f, -1.0f,
2604 1.0f, 1.0f, 1.0f,
2605 1.0f, -1.0f, 1.0f,
2606 1.0f, 1.0f, -1.0f,
2607 1.0f, -1.0f, 1.0f,
2608 1.0f, -1.0f, -1.0f,
2609
2610 // left
2611 -1.0f, 1.0f, 1.0f,
2612 -1.0f, 1.0f, -1.0f,
2613 -1.0f, -1.0f, -1.0f,
2614 -1.0f, 1.0f, 1.0f,
2615 -1.0f, -1.0f, -1.0f,
2616 -1.0f, -1.0f, 1.0f,
2617 };
2618 obj->colors = {
2619 // front
2620 0.4f, 0.4f, 0.4f,
2621 0.4f, 0.4f, 0.4f,
2622 0.4f, 0.4f, 0.4f,
2623 0.4f, 0.4f, 0.4f,
2624 0.4f, 0.4f, 0.4f,
2625 0.4f, 0.4f, 0.4f,
2626
2627 // top
2628 0.4f, 0.4f, 0.4f,
2629 0.4f, 0.4f, 0.4f,
2630 0.4f, 0.4f, 0.4f,
2631 0.4f, 0.4f, 0.4f,
2632 0.4f, 0.4f, 0.4f,
2633 0.4f, 0.4f, 0.4f,
2634
2635 // bottom
2636 0.4f, 0.4f, 0.4f,
2637 0.4f, 0.4f, 0.4f,
2638 0.4f, 0.4f, 0.4f,
2639 0.4f, 0.4f, 0.4f,
2640 0.4f, 0.4f, 0.4f,
2641 0.4f, 0.4f, 0.4f,
2642
2643 // back
2644 0.4f, 0.4f, 0.4f,
2645 0.4f, 0.4f, 0.4f,
2646 0.4f, 0.4f, 0.4f,
2647 0.4f, 0.4f, 0.4f,
2648 0.4f, 0.4f, 0.4f,
2649 0.4f, 0.4f, 0.4f,
2650
2651 // right
2652 0.4f, 0.4f, 0.4f,
2653 0.4f, 0.4f, 0.4f,
2654 0.4f, 0.4f, 0.4f,
2655 0.4f, 0.4f, 0.4f,
2656 0.4f, 0.4f, 0.4f,
2657 0.4f, 0.4f, 0.4f,
2658
2659 // left
2660 0.4f, 0.4f, 0.4f,
2661 0.4f, 0.4f, 0.4f,
2662 0.4f, 0.4f, 0.4f,
2663 0.4f, 0.4f, 0.4f,
2664 0.4f, 0.4f, 0.4f,
2665 0.4f, 0.4f, 0.4f,
2666 };
2667 obj->texcoords = { 0.0f };
2668
2669 mat4 T = translate(mat4(1.0f), pos);
2670 mat4 R = rotate(mat4(1.0f), 60.0f * (float)ONE_DEG_IN_RAD, vec3(1.0f, 1.0f, -1.0f));
2671 obj->model_base = T * R * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
2672
2673 obj->translate_mat = T;
2674
2675 initObject(obj);
2676 // This accounts for the scaling in model_base.
2677 // Dividing by 8 instead of 10 since the bounding radius algorithm
2678 // under-calculates the true value.
2679 // TODO: Once the intersection check with the sides of the asteroid is done,
2680 // this can be removed.
2681 obj->bounding_radius /= 8.0f;
2682
2683 return obj;
2684}
2685
2686// TODO: Maybe pass in startTime instead of calling glfwGetTime() here
2687ParticleEffect* createExplosion(mat4 model_mat) {
2688 ParticleEffect* obj = new ParticleEffect();
2689
2690 obj->type = TYPE_EXPLOSION;
2691
2692 initObject(obj);
2693
2694 obj->num_points = EXPLOSION_PARTICLE_COUNT;
2695 obj->model_base = model_mat;
2696 obj->startTime = glfwGetTime();
2697 obj->duration = 0.5f; // This is also hard-coded in the shader. TODO; Pass this to the shader in an indexable ubo.
2698
2699 obj->particleVelocities.clear();
2700 obj->particleTimes.clear();
2701 float t_accum = 0.0f; // start time
2702
2703 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
2704 obj->particleTimes.push_back(t_accum);
2705 t_accum += 0.01f;
2706
2707 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
2708 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
2709 obj->particleVelocities.push_back(randx);
2710 obj->particleVelocities.push_back(randy);
2711 obj->particleVelocities.push_back(0.0f);
2712 }
2713
2714 return obj;
2715}
Note: See TracBrowser for help on using the repository browser.