source: opengl-game/new-game.cpp@ 4a9416a

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

Create a pipeline and shaders to render explosions

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