source: opengl-game/new-game.cpp@ 52a02e6

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

Update OpenGLRef to more accurately reflect which code has been ported over to VulkanGame

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