source: opengl-game/new-game.cpp

feature/imgui-sdl
Last change on this file was c324d6a, checked in by Dmitry Portnoy <dportnoy@…>, 3 years ago

Make some minor updates to VulkanGame

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