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

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

Print a stack trace in the event of a crash. Currently, the code for this on Windows outputs the stack trace to stderr, not to a log file.

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