source: opengl-game/new-game.cpp@ 670c09a

feature/imgui-sdl
Last change on this file since 670c09a was e66fd66, checked in by Dmitry Portnoy <dportnoy@…>, 4 years ago

In OpenGLReference, change all enums to enum classes and update IMGUI to the latest version

  • Property mode set to 100644
File size: 87.8 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/imgui_impl_glfw.h"
12#include "IMGUI/imgui_impl_opengl3.h"
13
14#include <GL/glew.h>
15#include <GLFW/glfw3.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 window_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 ***/
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 string glsl_version = "#version 410";
336
337#ifdef MAC
338 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
339 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
340 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
341 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
342#else
343 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
344 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
345#endif
346
347 GLFWwindow* window = NULL;
348 GLFWmonitor* mon = NULL;
349
350 glfwWindowHint(GLFW_SAMPLES, 16);
351 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
352
353 if (FULLSCREEN) {
354 mon = glfwGetPrimaryMonitor();
355 const GLFWvidmode* vmode = glfwGetVideoMode(mon);
356
357 windowWidth = vmode->width;
358 windowHeight = vmode->height;
359 cout << "Fullscreen resolution " << vmode->width << "x" << vmode->height << endl;
360 }
361 window = glfwCreateWindow(windowWidth, windowHeight, "New OpenGL Game", mon, NULL);
362
363 if (!window) {
364 gl_log_err("ERROR: could not open window with GLFW3");
365 cerr << "ERROR: could not open window with GLFW3" << endl;
366 get_log() << "ERROR: could not open window with GLFW3" << endl;
367 glfwTerminate();
368 return 1;
369 }
370
371 glfwMakeContextCurrent(window);
372 glViewport(0, 0, windowWidth, windowHeight);
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, nullptr);
381 cout << "Bound debug callback" << endl;
382 } else {
383 cout << "OpenGL debug message callback is not supported" << endl;
384 }
385
386 srand(time(0));
387
388 /*
389 * RENDERING ALGORITHM NOTES:
390 *
391 * Basically, I need to split my objects into groups, so that each group fits into
392 * GL_MAX_UNIFORM_BLOCK_SIZE. I need to have an offset and a size for each group.
393 * Getting the offset is straitforward. The size may as well be GL_MAX_UNIFORM_BLOCK_SIZE
394 * for each group, since it seems that smaller sizes just round up to the nearest GL_MAX_UNIFORM_BLOCK_SIZE
395 *
396 * I'll need to have a loop inside my render loop that calls glBindBufferRange(GL_UNIFORM_BUFFER, ...
397 * for every 1024 objects and then draws all those objects with one glDraw call.
398 *
399 * Since I currently have very few objects, I'll wait to implement this until I have
400 * a reasonable number of objects always using the same shader.
401 */
402
403 GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT, MAX_UNIFORM_BLOCK_SIZE;
404 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &UNIFORM_BUFFER_OFFSET_ALIGNMENT);
405 glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &MAX_UNIFORM_BLOCK_SIZE);
406
407 MAX_UNIFORMS = MAX_UNIFORM_BLOCK_SIZE / sizeof(mat4);
408
409 cout << "UNIFORM_BUFFER_OFFSET_ALIGNMENT: " << UNIFORM_BUFFER_OFFSET_ALIGNMENT << endl;
410 cout << "MAX_UNIFORMS: " << MAX_UNIFORMS << endl;
411
412 // Setup Dear ImGui binding
413 IMGUI_CHECKVERSION();
414 ImGui::CreateContext();
415
416 ImGuiIO& io = ImGui::GetIO(); (void)io;
417 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
418 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
419
420 // Setup Dear ImGui style
421 ImGui::StyleColorsDark();
422 //ImGui::StyleColorsClassic();
423
424 // Setup Platform/Renderer backends
425 ImGui_ImplGlfw_InitForOpenGL(window, true);
426 ImGui_ImplOpenGL3_Init(glsl_version.c_str());
427
428 glfwSetMouseButtonCallback(window, mouse_button_callback);
429 glfwSetKeyCallback(window, key_callback);
430 glfwSetWindowSizeCallback(window, window_size_callback);
431/*** END OF REFACTORED CODE ***/
432
433 const GLubyte* renderer = glGetString(GL_RENDERER);
434 const GLubyte* version = glGetString(GL_VERSION);
435 cout << "Renderer: " << renderer << endl;
436 cout << "Supported OpenGL version: " << version << endl;
437
438 gl_log("Renderer: %s", renderer);
439 gl_log("Supported OpenGL version: %s", version);
440
441 get_log() << "Renderer: " << renderer << endl;
442 get_log() << "Supported OpenGL version: " << version << endl;
443
444 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
445
446 glEnable(GL_DEPTH_TEST);
447 glDepthFunc(GL_LESS);
448
449 glEnable(GL_CULL_FACE);
450 // glCullFace(GL_BACK);
451 // glFrontFace(GL_CW);
452
453 int x, y;
454 unsigned char* texImage = loadImage("laser.png", &x, &y);
455 if (texImage) {
456 cout << "Laser texture loaded successfully!" << endl;
457 cout << x << ", " << y << endl;
458 cout << "first 4 bytes are: " << texImage[0] << " " << texImage[1] << " " << texImage[2] << " " << texImage[3] << endl;
459 }
460
461 GLuint laserTex = 0;
462 glGenTextures(1, &laserTex);
463 glActiveTexture(GL_TEXTURE0);
464 glBindTexture(GL_TEXTURE_2D, laserTex);
465 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
466
467 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
468 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
469 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
470 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
471
472 /* RENDERING ALGORITHM
473 *
474 * Create a separate vbo for each of the following things:
475 * - points
476 * - colors
477 * - texture coordinates
478 * - selected colors
479 * - normals
480 * - indices into a ubo that stores a model matrix for each object
481 *
482 * Also, make a model matrix ubo, the entirety of which will be passed to the vertex shader.
483 * The vbo containing the correct index into the ubo (mentioned above) will be used to select
484 * the right model matrix for each point. The index in the vbo will be the saem for all points
485 * of any given object.
486 *
487 * Right now, the currently selected object is drawn using one color (specified in the selected
488 * colors vbo) regardless of whether it is normally rendering using colors or a texture. The selected
489 * object is rendering by binding the selected colors vbo in place of the colors vbo and using the colors
490 * shader. Then, the selected object is redrawn along with all other objects, but the depth buffer test
491 * prevents the unselected version of the object from appearing on the screen. This lets me render all the
492 * objects that use a particular shader using one glDrawArrays() call.
493 */
494
495/*** START OF REFACTORED CODE ***/
496 GLfloat laserColor[3] = {0.2f, 1.0f, 0.2f};
497 GLfloat curTime, prevTime, elapsedTime;
498
499 GLuint ubo = 0;
500 glGenBuffers(1, &ubo);
501
502 map<GLuint, BufferInfo> shaderBufferInfo;
503 map<ObjectType, ShaderModelGroup> modelGroups;
504
505 modelGroups[ObjectType::SHIP] = createModelGroup(
506 loadShaderProgram("gl-shaders/ship.vert", "gl-shaders/ship.frag"));
507 shaderBufferInfo[modelGroups[ObjectType::SHIP].shaderProgram] = BufferInfo(); // temporary
508
509 defineModelGroupAttrib(modelGroups[ObjectType::SHIP], "vertex_position", AttribType::POINT_VARYING,
510 3, GL_FLOAT, offset_of(&SceneObject::points));
511 defineModelGroupAttrib(modelGroups[ObjectType::SHIP], "vertex_color", AttribType::POINT_VARYING,
512 3, GL_FLOAT, offset_of(&SceneObject::colors));
513 defineModelGroupAttrib(modelGroups[ObjectType::SHIP], "vertex_normal", AttribType::POINT_VARYING,
514 3, GL_FLOAT, offset_of(&SceneObject::normals));
515 defineModelGroupAttrib(modelGroups[ObjectType::SHIP], "ubo_index", AttribType::OBJECT_VARYING,
516 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
517
518 defineModelGroupUniform(modelGroups[ObjectType::SHIP], "view", AttribType::UNIFORM,
519 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
520 defineModelGroupUniform(modelGroups[ObjectType::SHIP], "proj", AttribType::UNIFORM,
521 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
522
523 initModelGroupAttribs(modelGroups[ObjectType::SHIP]);
524
525 modelGroups[ObjectType::ASTEROID] = createModelGroup(
526 loadShaderProgram("gl-shaders/asteroid.vert", "gl-shaders/asteroid.frag"));
527 shaderBufferInfo[modelGroups[ObjectType::ASTEROID].shaderProgram] = BufferInfo(); // temporary
528
529 defineModelGroupAttrib(modelGroups[ObjectType::ASTEROID], "vertex_position", AttribType::POINT_VARYING,
530 3, GL_FLOAT, offset_of(&SceneObject::points));
531 defineModelGroupAttrib(modelGroups[ObjectType::ASTEROID], "vertex_color", AttribType::POINT_VARYING,
532 3, GL_FLOAT, offset_of(&SceneObject::colors));
533 defineModelGroupAttrib(modelGroups[ObjectType::ASTEROID], "vertex_normal", AttribType::POINT_VARYING,
534 3, GL_FLOAT, offset_of(&SceneObject::normals));
535 defineModelGroupAttrib(modelGroups[ObjectType::ASTEROID], "ubo_index", AttribType::OBJECT_VARYING,
536 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
537
538 defineModelGroupUniform(modelGroups[ObjectType::ASTEROID], "view", AttribType::UNIFORM,
539 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
540 defineModelGroupUniform(modelGroups[ObjectType::ASTEROID], "proj", AttribType::UNIFORM,
541 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
542
543 initModelGroupAttribs(modelGroups[ObjectType::ASTEROID]);
544
545 modelGroups[ObjectType::LASER] = createModelGroup(
546 loadShaderProgram("gl-shaders/laser.vert", "gl-shaders/laser.frag"));
547 shaderBufferInfo[modelGroups[ObjectType::LASER].shaderProgram] = BufferInfo(); // temporary
548
549 defineModelGroupAttrib(modelGroups[ObjectType::LASER], "vertex_position", AttribType::POINT_VARYING,
550 3, GL_FLOAT, offset_of(&SceneObject::points));
551 defineModelGroupAttrib(modelGroups[ObjectType::LASER], "vt", AttribType::POINT_VARYING,
552 2, GL_FLOAT, offset_of(&SceneObject::texcoords));
553 defineModelGroupAttrib(modelGroups[ObjectType::LASER], "ubo_index", AttribType::OBJECT_VARYING,
554 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
555
556 defineModelGroupUniform(modelGroups[ObjectType::LASER], "view", AttribType::UNIFORM,
557 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
558 defineModelGroupUniform(modelGroups[ObjectType::LASER], "proj", AttribType::UNIFORM,
559 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
560 defineModelGroupUniform(modelGroups[ObjectType::LASER], "laser_color", AttribType::UNIFORM,
561 1, UNIFORM_3F, laserColor);
562
563 initModelGroupAttribs(modelGroups[ObjectType::LASER]);
564
565 modelGroups[ObjectType::EXPLOSION] = createModelGroup(
566 loadShaderProgram("gl-shaders/explosion.vert", "gl-shaders/explosion.frag"));
567 shaderBufferInfo[modelGroups[ObjectType::EXPLOSION].shaderProgram] = BufferInfo(); // temporary
568
569 defineModelGroupAttrib(modelGroups[ObjectType::EXPLOSION], "v_i", AttribType::POINT_VARYING,
570 3, GL_FLOAT, offset_of(&ParticleEffect::particleVelocities));
571 defineModelGroupAttrib(modelGroups[ObjectType::EXPLOSION], "start_time", AttribType::POINT_VARYING,
572 1, GL_FLOAT, offset_of(&ParticleEffect::particleTimes));
573 defineModelGroupAttrib(modelGroups[ObjectType::EXPLOSION], "ubo_index", AttribType::OBJECT_VARYING,
574 1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
575
576 defineModelGroupUniform(modelGroups[ObjectType::EXPLOSION], "cur_time", AttribType::UNIFORM,
577 1, UNIFORM_1F, &curTime);
578 defineModelGroupUniform(modelGroups[ObjectType::EXPLOSION], "view", AttribType::UNIFORM,
579 1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
580 defineModelGroupUniform(modelGroups[ObjectType::EXPLOSION], "proj", AttribType::UNIFORM,
581 1, UNIFORM_MATRIX_4F, value_ptr(proj_mat));
582
583 initModelGroupAttribs(modelGroups[ObjectType::EXPLOSION]);
584
585 cam_pos = vec3(0.0f, 0.0f, 2.0f);
586 float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
587 float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
588
589 // player ship
590 objects.push_back(createShip());
591
592 populateBuffers(objects, shaderBufferInfo, modelGroups, ubo);
593
594 float cam_speed = 1.0f;
595 float cam_yaw_speed = radians(60.0f);
596 float cam_pitch_speed = radians(60.0f);
597
598 // glm::lookAt can create the view matrix
599 // glm::perspective can create the projection matrix
600
601 mat4 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
602 mat4 yaw_mat = rotate(mat4(1.0f), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
603 mat4 pitch_mat = rotate(mat4(1.0f), -cam_pitch, vec3(1.0f, 0.0f, 0.0f));
604 mat4 R = pitch_mat * yaw_mat;
605 view_mat = R*T;
606
607 // TODO: Create a function to construct the projection matrix
608 // (Maybe I should just use glm::perspective, after making sure it matches what I have now)
609 float fov = radians(67.0f);
610 float aspect = (float)windowWidth / (float)windowHeight;
611
612 float range = tan(fov * 0.5f) * NEAR_CLIP;
613 float Sx = NEAR_CLIP / (range * aspect);
614 float Sy = NEAR_CLIP / range;
615 float Sz = -(FAR_CLIP + NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
616 float Pz = -(2.0f * FAR_CLIP * NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
617
618 float proj_arr[] = {
619 Sx, 0.0f, 0.0f, 0.0f,
620 0.0f, Sy, 0.0f, 0.0f,
621 0.0f, 0.0f, Sz, -1.0f,
622 0.0f, 0.0f, Pz, 0.0f,
623 };
624 proj_mat = make_mat4(proj_arr);
625
626 /* TODO: Fix the UBO binding code based on the following forum post (in order to support multiple ubos):
627 (Also, I bookmarked a great explanation of this under )
628
629 CHECK MY OpenGL BOOKMARK CALLED "Learn OpenGL: Advanced GLSL"
630
631 No, you're misunderstanding how this works. UBO binding works exactly like texture object binding.
632
633 The OpenGL context has a number of slots for binding UBOs. There are GL_MAX_UNIFORM_BUFFER_BINDINGS number of
634 slots for UBO binding.
635
636 Uniform Blocks in a program can be set to use one of the slots in the context. You do this by first querying
637 the block index using the block name (glGetUniformBlockIndex). This is similar to how you need to use
638 glGetUniformLocation in order to set a uniform's value with glUniform. Block indices, like uniform locations,
639 are specific to a program.
640
641 Once you have the block index, you use glUniformBlockBinding to set that specific program to use a particular
642 uniform buffer slot in the context.
643
644 Let's say you have a global UBO that you want to use for every program. To make using it easier, you want to
645 bind it just once.
646
647 So first, you pick a uniform buffer slot in the context, one that always will refer to this UBO. Let's say
648 you pick slot 8.
649
650 When you build a program object that may use this global uniform buffer, what you do is quite simple. First,
651 after linking the program, call glGetUniformBlockIndex(program, "NameOfGlobalUniformBlock"). If you get back
652 GL_INVALID_INDEX, then you know that the global uniform block isn't used in that program. Otherwise you get
653 back a block index.
654
655 If you got a valid block index, then you call glUniformBlockBinding(program, uniformBlockIndex, 8). Remember
656 that 8 is the uniform buffer context slot that we selected earlier. This causes this particular program to
657 use uniform buffer slot #8 to find the buffer for "NameOfGlobalUniformBlock".
658
659 Finally, to set the actual buffer in the context, call glBindBufferRange(GL_UNIFORM_BUFFER, 8,
660 bufferObjectName, offset, size);
661 */
662
663 GLuint ub_binding_point = 0;
664
665 GLuint ship_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[ObjectType::SHIP].shaderProgram, "models");
666
667 GLuint asteroid_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[ObjectType::ASTEROID].shaderProgram, "models");
668
669 GLuint laser_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[ObjectType::LASER].shaderProgram, "models");
670
671 GLuint explosion_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[ObjectType::EXPLOSION].shaderProgram, "models");
672
673
674 glUseProgram(modelGroups[ObjectType::SHIP].shaderProgram);
675 bindUniformData(modelGroups[ObjectType::SHIP].attribs["view"]);
676 bindUniformData(modelGroups[ObjectType::SHIP].attribs["proj"]);
677
678 glUniformBlockBinding(modelGroups[ObjectType::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[ObjectType::ASTEROID].shaderProgram);
683 bindUniformData(modelGroups[ObjectType::ASTEROID].attribs["view"]);
684 bindUniformData(modelGroups[ObjectType::ASTEROID].attribs["proj"]);
685
686 glUniformBlockBinding(modelGroups[ObjectType::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[ObjectType::LASER].shaderProgram);
694 bindUniformData(modelGroups[ObjectType::LASER].attribs["view"]);
695 bindUniformData(modelGroups[ObjectType::LASER].attribs["proj"]);
696 bindUniformData(modelGroups[ObjectType::LASER].attribs["laser_color"]);
697
698 glUniformBlockBinding(modelGroups[ObjectType::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
701
702 glUseProgram(modelGroups[ObjectType::EXPLOSION].shaderProgram);
703 bindUniformData(modelGroups[ObjectType::EXPLOSION].attribs["view"]);
704 bindUniformData(modelGroups[ObjectType::EXPLOSION].attribs["proj"]);
705
706 glUniformBlockBinding(modelGroups[ObjectType::EXPLOSION].shaderProgram, explosion_sp_models_ub_index, ub_binding_point);
707 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
708/*** END OF REFACTORED CODE ***/
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 == ObjectType::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/*** START OF REFACTORED CODE ***/
884 addObjectToScene(createExplosion(model_mat), shaderBufferInfo, modelGroups, ubo);
885 }
886 } else if (objects[i]->type == ObjectType::EXPLOSION) {
887 ParticleEffect* explosion = (ParticleEffect*)objects[i];
888 if (glfwGetTime() >= explosion->startTime + explosion->duration) {
889 removeObjectFromScene(*objects[i], ubo);
890 }
891 }
892 }
893 }
894
895 if (leftLaser != NULL && !leftLaser->deleted) {
896 updateLaserTarget(leftLaser, objects, modelGroups[ObjectType::LASER], modelGroups[ObjectType::ASTEROID].shaderProgram);
897 }
898 if (rightLaser != NULL && !rightLaser->deleted) {
899 updateLaserTarget(rightLaser, objects, modelGroups[ObjectType::LASER], modelGroups[ObjectType::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[ObjectType::SHIP].shaderProgram);
972 bindUniformData(modelGroups[ObjectType::SHIP].attribs["view"]);
973
974 glUseProgram(modelGroups[ObjectType::LASER].shaderProgram);
975 bindUniformData(modelGroups[ObjectType::LASER].attribs["view"]);
976
977 cam_moved = false;
978 }
979
980 glUseProgram(modelGroups[ObjectType::EXPLOSION].shaderProgram);
981 bindUniformData(modelGroups[ObjectType::EXPLOSION].attribs["cur_time"]);
982
983 // Render scene
984
985 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
986/*** END OF REFACTORED CODE ***/
987
988 switch (curState) {
989 case State::MAIN_MENU:
990 renderMainMenu();
991 renderMainMenuGui();
992 break;
993 case State::GAME:
994 renderScene(modelGroups, ubo);
995 renderSceneGui(valueLists);
996 break;
997 }
998
999 glfwSwapBuffers(window);
1000 }
1001
1002 ImGui_ImplOpenGL3_Shutdown();
1003 ImGui_ImplGlfw_Shutdown();
1004 ImGui::DestroyContext();
1005
1006 glfwDestroyWindow(window);
1007 glfwTerminate();
1008
1009 close_log();
1010
1011 // free memory
1012
1013 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
1014 delete *it;
1015 }
1016
1017 return 0;
1018}
1019
1020void glfw_error_callback(int error, const char* description) {
1021 gl_log_err("GLFW ERROR: code %i msg: %s", error, description);
1022 cerr << "GLFW ERROR: code " << error << " msg: " << description << endl;
1023 get_log() << "GLFW ERROR: code " << error << " msg: " << description << endl;
1024}
1025
1026void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
1027 double mouse_x, mouse_y;
1028 glfwGetCursorPos(window, &mouse_x, &mouse_y);
1029
1030 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
1031 cout << "Mouse clicked (" << mouse_x << "," << mouse_y << ")" << endl;
1032 selectedObject = NULL;
1033
1034 float x = (2.0f*mouse_x) / windowWidth - 1.0f;
1035 float y = 1.0f - (2.0f*mouse_y) / windowHeight;
1036
1037 cout << "x: " << x << ", y: " << y << endl;
1038
1039 vec4 ray_clip = vec4(x, y, -1.0f, 1.0f);
1040 vec4 ray_eye = inverse(proj_mat) * ray_clip;
1041 ray_eye = vec4(vec2(ray_eye), -1.0f, 1.0f);
1042 vec4 ray_world = inverse(view_mat) * ray_eye;
1043
1044 vec4 click_point;
1045 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
1046 SceneObject* closest_object = NULL;
1047
1048 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
1049 if ((*it)->type == ObjectType::LASER) continue;
1050 for (unsigned int p_idx = 0; p_idx < (*it)->points.size(); p_idx += 9) {
1051 if (faceClicked(
1052 {
1053 vec3((*it)->points[p_idx], (*it)->points[p_idx + 1], (*it)->points[p_idx + 2]),
1054 vec3((*it)->points[p_idx + 3], (*it)->points[p_idx + 4], (*it)->points[p_idx + 5]),
1055 vec3((*it)->points[p_idx + 6], (*it)->points[p_idx + 7], (*it)->points[p_idx + 8]),
1056 },
1057 *it, ray_world, vec4(cam_pos, 1.0f), click_point
1058 )) {
1059 click_point = view_mat * click_point;
1060
1061 if (-NEAR_CLIP >= click_point.z && click_point.z > -FAR_CLIP && click_point.z > closest_point.z) {
1062 closest_point = vec3(click_point);
1063 closest_object = *it;
1064 }
1065 }
1066 }
1067 }
1068
1069 if (closest_object == NULL) {
1070 cout << "No object was clicked" << endl;
1071 } else {
1072 clickedObject = closest_object;
1073 cout << "Clicked object: " << clickedObject->id << endl;
1074 }
1075 }
1076}
1077
1078void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
1079 key_state[key] = action;
1080
1081 // should be true for GLFW_PRESS and GLFW_REPEAT
1082 key_down[key] = (action != GLFW_RELEASE);
1083}
1084
1085/*** START OF REFACTORED CODE ***/
1086void window_size_callback(GLFWwindow* window, int width, int height) {
1087 cout << "Window resized to (" << width << ", " << height << ")" << endl;
1088
1089 windowWidth = width;
1090 windowHeight = height;
1091
1092 // TODO: Ideally, remove the window title bar when the window is maximized
1093 // Check https://github.com/glfw/glfw/issues/778
1094
1095 // This requires glfw3.3. I think I have to upgrade
1096 // Doesn't seem to be needed in OSX and also causes a segfault there
1097 //glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE);
1098}
1099/*** END OF REFACTORED CODE ***/
1100
1101void APIENTRY debugGlCallback(
1102 GLenum source,
1103 GLenum type,
1104 GLuint id,
1105 GLenum severity,
1106 GLsizei length,
1107 const GLchar* message,
1108 const void* userParam
1109) {
1110 string strMessage(message);
1111
1112 // TODO: Use C++ strings directly
1113 char source_str[2048];
1114 char type_str[2048];
1115 char severity_str[2048];
1116
1117 switch (source) {
1118 case 0x8246:
1119 strcpy(source_str, "API");
1120 break;
1121 case 0x8247:
1122 strcpy(source_str, "WINDOW_SYSTEM");
1123 break;
1124 case 0x8248:
1125 strcpy(source_str, "SHADER_COMPILER");
1126 break;
1127 case 0x8249:
1128 strcpy(source_str, "THIRD_PARTY");
1129 break;
1130 case 0x824A:
1131 strcpy(source_str, "APPLICATION");
1132 break;
1133 case 0x824B:
1134 strcpy(source_str, "OTHER");
1135 break;
1136 default:
1137 strcpy(source_str, "undefined");
1138 break;
1139 }
1140
1141 switch (type) {
1142 case 0x824C:
1143 strcpy(type_str, "ERROR");
1144 break;
1145 case 0x824D:
1146 strcpy(type_str, "DEPRECATED_BEHAVIOR");
1147 break;
1148 case 0x824E:
1149 strcpy(type_str, "UNDEFINED_BEHAVIOR");
1150 break;
1151 case 0x824F:
1152 strcpy(type_str, "PORTABILITY");
1153 break;
1154 case 0x8250:
1155 strcpy(type_str, "PERFORMANCE");
1156 break;
1157 case 0x8251:
1158 strcpy(type_str, "OTHER");
1159 break;
1160 case 0x8268:
1161 strcpy(type_str, "MARKER");
1162 break;
1163 case 0x8269:
1164 strcpy(type_str, "PUSH_GROUP");
1165 break;
1166 case 0x826A:
1167 strcpy(type_str, "POP_GROUP");
1168 break;
1169 default:
1170 strcpy(type_str, "undefined");
1171 break;
1172 }
1173 switch (severity) {
1174 case 0x9146:
1175 strcpy(severity_str, "HIGH");
1176 break;
1177 case 0x9147:
1178 strcpy(severity_str, "MEDIUM");
1179 break;
1180 case 0x9148:
1181 strcpy(severity_str, "LOW");
1182 break;
1183 case 0x826B:
1184 strcpy(severity_str, "NOTIFICATION");
1185 break;
1186 default:
1187 strcpy(severity_str, "undefined");
1188 break;
1189 }
1190
1191 if (string(severity_str) != "NOTIFICATION") {
1192 cout << "OpenGL Error!!!" << endl;
1193 cout << "Source: " << string(source_str) << endl;
1194 cout << "Type: " << string(type_str) << endl;
1195 cout << "Severity: " << string(severity_str) << endl;
1196 cout << strMessage << endl;
1197 }
1198}
1199
1200
1201/*** START OF REFACTORED CODE ***/
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/*** END OF REFACTORED CODE ***/
1282
1283bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point) {
1284 // LINE EQUATION: P = O + Dt
1285 // O = cam
1286 // D = ray_world
1287
1288 // PLANE EQUATION: P dot n + d = 0
1289 // n is the normal vector
1290 // d is the offset from the origin
1291
1292 // Take the cross-product of two vectors on the plane to get the normal
1293 vec3 v1 = points[1] - points[0];
1294 vec3 v2 = points[2] - points[0];
1295
1296 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);
1297
1298 vec3 local_ray = vec3(inverse(obj->model_mat) * world_ray);
1299 vec3 local_cam = vec3(inverse(obj->model_mat) * cam);
1300
1301 local_ray = local_ray - local_cam;
1302
1303 float d = -glm::dot(points[0], normal);
1304 float t = -(glm::dot(local_cam, normal) + d) / glm::dot(local_ray, normal);
1305
1306 vec3 intersection = local_cam + t*local_ray;
1307
1308 if (insideTriangle(intersection, points)) {
1309 click_point = obj->model_mat * vec4(intersection, 1.0f);
1310 return true;
1311 } else {
1312 return false;
1313 }
1314}
1315
1316bool insideTriangle(vec3 p, array<vec3, 3> triangle_points) {
1317 vec3 v21 = triangle_points[1] - triangle_points[0];
1318 vec3 v31 = triangle_points[2] - triangle_points[0];
1319 vec3 pv1 = p - triangle_points[0];
1320
1321 float y = (pv1.y*v21.x - pv1.x*v21.y) / (v31.y*v21.x - v31.x*v21.y);
1322 float x = (pv1.x-y*v31.x) / v21.x;
1323
1324 return x > 0.0f && y > 0.0f && x+y < 1.0f;
1325}
1326
1327/*** START OF REFACTORED CODE ***/
1328// TODO: Pass a reference, not a pointer
1329void initObject(SceneObject* obj) {
1330 obj->id = objects.size(); // currently unused
1331 obj->num_points = obj->points.size() / 3;
1332 obj->model_transform = mat4(1.0f);
1333 obj->deleted = false;
1334
1335 obj->normals.reserve(obj->points.size());
1336 for (unsigned int i = 0; i < obj->points.size(); i += 9) {
1337 vec3 point1 = vec3(obj->points[i], obj->points[i + 1], obj->points[i + 2]);
1338 vec3 point2 = vec3(obj->points[i + 3], obj->points[i + 4], obj->points[i + 5]);
1339 vec3 point3 = vec3(obj->points[i + 6], obj->points[i + 7], obj->points[i + 8]);
1340
1341 vec3 normal = normalize(cross(point2 - point1, point3 - point1));
1342
1343 // Add the same normal for all 3 points
1344 for (int j = 0; j < 3; j++) {
1345 obj->normals.push_back(normal.x);
1346 obj->normals.push_back(normal.y);
1347 obj->normals.push_back(normal.z);
1348 }
1349 }
1350
1351 if (obj->type == ObjectType::SHIP || obj->type == ObjectType::ASTEROID) {
1352 calculateObjectBoundingBox(obj);
1353
1354 obj->bounding_center = vec3(obj->translate_mat * vec4(obj->bounding_center, 1.0f));
1355 }
1356}
1357
1358// TODO: Check if I can pass in a reference to obj instead (do this for all other functions as well)
1359void addObjectToScene(SceneObject* obj,
1360 map<GLuint, BufferInfo>& shaderBufferInfo,
1361 map<ObjectType, ShaderModelGroup>& modelGroups,
1362 GLuint ubo) {
1363 objects.push_back(obj);
1364
1365 BufferInfo* bufferInfo = &shaderBufferInfo[modelGroups[obj->type].shaderProgram];
1366
1367 // Check if the buffers aren't large enough to fit the new object and, if so, call
1368 // populateBuffers() to resize and repopupulate them
1369 if ((modelGroups[obj->type].vboCapacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
1370 bufferInfo->ubo_capacity <= bufferInfo->ubo_offset)) {
1371
1372 if (leftLaser != NULL && leftLaser->deleted) {
1373 leftLaser = NULL;
1374 }
1375 if (rightLaser != NULL && rightLaser->deleted) {
1376 rightLaser = NULL;
1377 }
1378
1379 populateBuffers(objects, shaderBufferInfo, modelGroups, ubo);
1380 } else {
1381 copyObjectDataToBuffers(*objects.back(), shaderBufferInfo, modelGroups, ubo);
1382 }
1383}
1384
1385void removeObjectFromScene(SceneObject& obj, GLuint ubo) {
1386 if (!obj.deleted) {
1387 // Move the object outside the render bounds of the scene so it doesn't get rendered
1388 // TODO: Find a better way of hiding the object until the next time buffers are repopulated
1389 transformObject(obj, translate(mat4(1.0f), vec3(0.0f, 0.0f, FAR_CLIP * 1000.0f)), ubo);
1390 obj.deleted = true;
1391 }
1392}
1393
1394// TODO: Pass a reference, not a pointer
1395void calculateObjectBoundingBox(SceneObject* obj) {
1396 GLfloat min_x = obj->points[0];
1397 GLfloat max_x = obj->points[0];
1398 GLfloat min_y = obj->points[1];
1399 GLfloat max_y = obj->points[1];
1400 GLfloat min_z = obj->points[2];
1401 GLfloat max_z = obj->points[2];
1402
1403 // start from the second point
1404 for (unsigned int i = 3; i < obj->points.size(); i += 3) {
1405 if (min_x > obj->points[i]) {
1406 min_x = obj->points[i];
1407 }
1408 else if (max_x < obj->points[i]) {
1409 max_x = obj->points[i];
1410 }
1411
1412 if (min_y > obj->points[i + 1]) {
1413 min_y = obj->points[i + 1];
1414 }
1415 else if (max_y < obj->points[i + 1]) {
1416 max_y = obj->points[i + 1];
1417 }
1418
1419 if (min_z > obj->points[i + 2]) {
1420 min_z = obj->points[i + 2];
1421 }
1422 else if (max_z < obj->points[i + 2]) {
1423 max_z = obj->points[i + 2];
1424 }
1425 }
1426
1427 obj->bounding_center = vec3((min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f);
1428
1429 GLfloat radius_x = max_x - obj->bounding_center.x;
1430 GLfloat radius_y = max_y - obj->bounding_center.y;
1431 GLfloat radius_z = max_z - obj->bounding_center.z;
1432
1433 // TODO: This actually underestimates the radius. Might need to be fixed at some point.
1434 // TODO: Does not take into account any scaling in the model matrix
1435 obj->bounding_radius = radius_x;
1436 if (obj->bounding_radius < radius_y)
1437 obj->bounding_radius = radius_y;
1438 if (obj->bounding_radius < radius_z)
1439 obj->bounding_radius = radius_z;
1440
1441 for (unsigned int i = 0; i < obj->points.size(); i += 3) {
1442 obj->points[i] -= obj->bounding_center.x;
1443 obj->points[i + 1] -= obj->bounding_center.y;
1444 obj->points[i + 2] -= obj->bounding_center.z;
1445 }
1446
1447 obj->bounding_center = vec3(0.0f, 0.0f, 0.0f);
1448}
1449
1450SceneObject* createShip() {
1451 SceneObject* ship = new SceneObject();
1452
1453 ship->type = ObjectType::SHIP;
1454
1455 ship->points = {
1456 //back
1457 -0.5f, 0.3f, 0.0f,
1458 -0.5f, 0.0f, 0.0f,
1459 0.5f, 0.0f, 0.0f,
1460 -0.5f, 0.3f, 0.0f,
1461 0.5f, 0.0f, 0.0f,
1462 0.5f, 0.3f, 0.0f,
1463
1464 // left back
1465 -0.5f, 0.3f, -2.0f,
1466 -0.5f, 0.0f, -2.0f,
1467 -0.5f, 0.0f, 0.0f,
1468 -0.5f, 0.3f, -2.0f,
1469 -0.5f, 0.0f, 0.0f,
1470 -0.5f, 0.3f, 0.0f,
1471
1472 // right back
1473 0.5f, 0.3f, 0.0f,
1474 0.5f, 0.0f, 0.0f,
1475 0.5f, 0.0f, -2.0f,
1476 0.5f, 0.3f, 0.0f,
1477 0.5f, 0.0f, -2.0f,
1478 0.5f, 0.3f, -2.0f,
1479
1480 // left mid
1481 -0.25f, 0.3f, -3.0f,
1482 -0.25f, 0.0f, -3.0f,
1483 -0.5f, 0.0f, -2.0f,
1484 -0.25f, 0.3f, -3.0f,
1485 -0.5f, 0.0f, -2.0f,
1486 -0.5f, 0.3f, -2.0f,
1487
1488 // right mid
1489 0.5f, 0.3f, -2.0f,
1490 0.5f, 0.0f, -2.0f,
1491 0.25f, 0.0f, -3.0f,
1492 0.5f, 0.3f, -2.0f,
1493 0.25f, 0.0f, -3.0f,
1494 0.25f, 0.3f, -3.0f,
1495
1496 // left front
1497 0.0f, 0.0f, -3.5f,
1498 -0.25f, 0.0f, -3.0f,
1499 -0.25f, 0.3f, -3.0f,
1500
1501 // right front
1502 0.25f, 0.3f, -3.0f,
1503 0.25f, 0.0f, -3.0f,
1504 0.0f, 0.0f, -3.5f,
1505
1506 // top back
1507 -0.5f, 0.3f, -2.0f,
1508 -0.5f, 0.3f, 0.0f,
1509 0.5f, 0.3f, 0.0f,
1510 -0.5f, 0.3f, -2.0f,
1511 0.5f, 0.3f, 0.0f,
1512 0.5f, 0.3f, -2.0f,
1513
1514 // bottom back
1515 -0.5f, 0.0f, 0.0f,
1516 -0.5f, 0.0f, -2.0f,
1517 0.5f, 0.0f, 0.0f,
1518 0.5f, 0.0f, 0.0f,
1519 -0.5f, 0.0f, -2.0f,
1520 0.5f, 0.0f, -2.0f,
1521
1522 // top mid
1523 -0.25f, 0.3f, -3.0f,
1524 -0.5f, 0.3f, -2.0f,
1525 0.5f, 0.3f, -2.0f,
1526 -0.25f, 0.3f, -3.0f,
1527 0.5f, 0.3f, -2.0f,
1528 0.25f, 0.3f, -3.0f,
1529
1530 // bottom mid
1531 -0.5f, 0.0f, -2.0f,
1532 -0.25f, 0.0f, -3.0f,
1533 0.5f, 0.0f, -2.0f,
1534 0.5f, 0.0f, -2.0f,
1535 -0.25f, 0.0f, -3.0f,
1536 0.25f, 0.0f, -3.0f,
1537
1538 // top front
1539 -0.25f, 0.3f, -3.0f,
1540 0.25f, 0.3f, -3.0f,
1541 0.0f, 0.0f, -3.5f,
1542
1543 // bottom front
1544 0.25f, 0.0f, -3.0f,
1545 -0.25f, 0.0f, -3.0f,
1546 0.0f, 0.0f, -3.5f,
1547
1548 // left wing start back
1549 -1.5f, 0.3f, 0.0f,
1550 -1.5f, 0.0f, 0.0f,
1551 -0.5f, 0.0f, 0.0f,
1552 -1.5f, 0.3f, 0.0f,
1553 -0.5f, 0.0f, 0.0f,
1554 -0.5f, 0.3f, 0.0f,
1555
1556 // left wing start top
1557 -0.5f, 0.3f, -0.3f,
1558 -1.3f, 0.3f, -0.3f,
1559 -1.5f, 0.3f, 0.0f,
1560 -0.5f, 0.3f, -0.3f,
1561 -1.5f, 0.3f, 0.0f,
1562 -0.5f, 0.3f, 0.0f,
1563
1564 // left wing start front
1565 -0.5f, 0.3f, -0.3f,
1566 -0.5f, 0.0f, -0.3f,
1567 -1.3f, 0.0f, -0.3f,
1568 -0.5f, 0.3f, -0.3f,
1569 -1.3f, 0.0f, -0.3f,
1570 -1.3f, 0.3f, -0.3f,
1571
1572 // left wing start bottom
1573 -0.5f, 0.0f, 0.0f,
1574 -1.5f, 0.0f, 0.0f,
1575 -1.3f, 0.0f, -0.3f,
1576 -0.5f, 0.0f, 0.0f,
1577 -1.3f, 0.0f, -0.3f,
1578 -0.5f, 0.0f, -0.3f,
1579
1580 // left wing end outside
1581 -1.5f, 0.3f, 0.0f,
1582 -2.2f, 0.15f, -0.8f,
1583 -1.5f, 0.0f, 0.0f,
1584
1585 // left wing end top
1586 -1.3f, 0.3f, -0.3f,
1587 -2.2f, 0.15f, -0.8f,
1588 -1.5f, 0.3f, 0.0f,
1589
1590 // left wing end front
1591 -1.3f, 0.0f, -0.3f,
1592 -2.2f, 0.15f, -0.8f,
1593 -1.3f, 0.3f, -0.3f,
1594
1595 // left wing end bottom
1596 -1.5f, 0.0f, 0.0f,
1597 -2.2f, 0.15f, -0.8f,
1598 -1.3f, 0.0f, -0.3f,
1599
1600 // right wing start back
1601 1.5f, 0.0f, 0.0f,
1602 1.5f, 0.3f, 0.0f,
1603 0.5f, 0.0f, 0.0f,
1604 0.5f, 0.0f, 0.0f,
1605 1.5f, 0.3f, 0.0f,
1606 0.5f, 0.3f, 0.0f,
1607
1608 // right wing start top
1609 1.3f, 0.3f, -0.3f,
1610 0.5f, 0.3f, -0.3f,
1611 1.5f, 0.3f, 0.0f,
1612 1.5f, 0.3f, 0.0f,
1613 0.5f, 0.3f, -0.3f,
1614 0.5f, 0.3f, 0.0f,
1615
1616 // right wing start front
1617 0.5f, 0.0f, -0.3f,
1618 0.5f, 0.3f, -0.3f,
1619 1.3f, 0.0f, -0.3f,
1620 1.3f, 0.0f, -0.3f,
1621 0.5f, 0.3f, -0.3f,
1622 1.3f, 0.3f, -0.3f,
1623
1624 // right wing start bottom
1625 1.5f, 0.0f, 0.0f,
1626 0.5f, 0.0f, 0.0f,
1627 1.3f, 0.0f, -0.3f,
1628 1.3f, 0.0f, -0.3f,
1629 0.5f, 0.0f, 0.0f,
1630 0.5f, 0.0f, -0.3f,
1631
1632 // right wing end outside
1633 2.2f, 0.15f, -0.8f,
1634 1.5f, 0.3f, 0.0f,
1635 1.5f, 0.0f, 0.0f,
1636
1637 // right wing end top
1638 2.2f, 0.15f, -0.8f,
1639 1.3f, 0.3f, -0.3f,
1640 1.5f, 0.3f, 0.0f,
1641
1642 // right wing end front
1643 2.2f, 0.15f, -0.8f,
1644 1.3f, 0.0f, -0.3f,
1645 1.3f, 0.3f, -0.3f,
1646
1647 // right wing end bottom
1648 2.2f, 0.15f, -0.8f,
1649 1.5f, 0.0f, 0.0f,
1650 1.3f, 0.0f, -0.3f,
1651 };
1652 ship->colors = {
1653 0.0f, 0.0f, 0.3f,
1654 0.0f, 0.0f, 0.3f,
1655 0.0f, 0.0f, 0.3f,
1656 0.0f, 0.0f, 0.3f,
1657 0.0f, 0.0f, 0.3f,
1658 0.0f, 0.0f, 0.3f,
1659
1660 0.0f, 0.0f, 0.3f,
1661 0.0f, 0.0f, 0.3f,
1662 0.0f, 0.0f, 0.3f,
1663 0.0f, 0.0f, 0.3f,
1664 0.0f, 0.0f, 0.3f,
1665 0.0f, 0.0f, 0.3f,
1666
1667 0.0f, 0.0f, 0.3f,
1668 0.0f, 0.0f, 0.3f,
1669 0.0f, 0.0f, 0.3f,
1670 0.0f, 0.0f, 0.3f,
1671 0.0f, 0.0f, 0.3f,
1672 0.0f, 0.0f, 0.3f,
1673
1674 0.0f, 0.0f, 0.3f,
1675 0.0f, 0.0f, 0.3f,
1676 0.0f, 0.0f, 0.3f,
1677 0.0f, 0.0f, 0.3f,
1678 0.0f, 0.0f, 0.3f,
1679 0.0f, 0.0f, 0.3f,
1680
1681 0.0f, 0.0f, 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 0.0f, 0.0f, 0.3f,
1687
1688 0.0f, 0.0f, 1.0f,
1689 0.0f, 0.0f, 1.0f,
1690 0.0f, 0.0f, 1.0f,
1691
1692 0.0f, 0.0f, 1.0f,
1693 0.0f, 0.0f, 1.0f,
1694 0.0f, 0.0f, 1.0f,
1695
1696 0.0f, 0.0f, 1.0f,
1697 0.0f, 0.0f, 1.0f,
1698 0.0f, 0.0f, 1.0f,
1699 0.0f, 0.0f, 1.0f,
1700 0.0f, 0.0f, 1.0f,
1701 0.0f, 0.0f, 1.0f,
1702
1703 0.0f, 0.0f, 1.0f,
1704 0.0f, 0.0f, 1.0f,
1705 0.0f, 0.0f, 1.0f,
1706 0.0f, 0.0f, 1.0f,
1707 0.0f, 0.0f, 1.0f,
1708 0.0f, 0.0f, 1.0f,
1709
1710 0.0f, 0.0f, 1.0f,
1711 0.0f, 0.0f, 1.0f,
1712 0.0f, 0.0f, 1.0f,
1713 0.0f, 0.0f, 1.0f,
1714 0.0f, 0.0f, 1.0f,
1715 0.0f, 0.0f, 1.0f,
1716
1717 0.0f, 0.0f, 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 0.0f, 0.0f, 1.0f,
1723
1724 0.0f, 0.0f, 0.3f,
1725 0.0f, 0.0f, 0.3f,
1726 0.0f, 0.0f, 0.3f,
1727
1728 0.0f, 0.0f, 0.3f,
1729 0.0f, 0.0f, 0.3f,
1730 0.0f, 0.0f, 0.3f,
1731
1732 0.0f, 0.0f, 0.3f,
1733 0.0f, 0.0f, 0.3f,
1734 0.0f, 0.0f, 0.3f,
1735 0.0f, 0.0f, 0.3f,
1736 0.0f, 0.0f, 0.3f,
1737 0.0f, 0.0f, 0.3f,
1738
1739 0.0f, 0.0f, 0.3f,
1740 0.0f, 0.0f, 0.3f,
1741 0.0f, 0.0f, 0.3f,
1742 0.0f, 0.0f, 0.3f,
1743 0.0f, 0.0f, 0.3f,
1744 0.0f, 0.0f, 0.3f,
1745
1746 0.0f, 0.0f, 0.3f,
1747 0.0f, 0.0f, 0.3f,
1748 0.0f, 0.0f, 0.3f,
1749 0.0f, 0.0f, 0.3f,
1750 0.0f, 0.0f, 0.3f,
1751 0.0f, 0.0f, 0.3f,
1752
1753 0.0f, 0.0f, 0.3f,
1754 0.0f, 0.0f, 0.3f,
1755 0.0f, 0.0f, 0.3f,
1756 0.0f, 0.0f, 0.3f,
1757 0.0f, 0.0f, 0.3f,
1758 0.0f, 0.0f, 0.3f,
1759
1760 0.0f, 0.0f, 0.3f,
1761 0.0f, 0.0f, 0.3f,
1762 0.0f, 0.0f, 0.3f,
1763
1764 0.0f, 0.0f, 0.3f,
1765 0.0f, 0.0f, 0.3f,
1766 0.0f, 0.0f, 0.3f,
1767
1768 0.0f, 0.0f, 0.3f,
1769 0.0f, 0.0f, 0.3f,
1770 0.0f, 0.0f, 0.3f,
1771
1772 0.0f, 0.0f, 0.3f,
1773 0.0f, 0.0f, 0.3f,
1774 0.0f, 0.0f, 0.3f,
1775
1776 0.0f, 0.0f, 0.3f,
1777 0.0f, 0.0f, 0.3f,
1778 0.0f, 0.0f, 0.3f,
1779 0.0f, 0.0f, 0.3f,
1780 0.0f, 0.0f, 0.3f,
1781 0.0f, 0.0f, 0.3f,
1782
1783 0.0f, 0.0f, 0.3f,
1784 0.0f, 0.0f, 0.3f,
1785 0.0f, 0.0f, 0.3f,
1786 0.0f, 0.0f, 0.3f,
1787 0.0f, 0.0f, 0.3f,
1788 0.0f, 0.0f, 0.3f,
1789
1790 0.0f, 0.0f, 0.3f,
1791 0.0f, 0.0f, 0.3f,
1792 0.0f, 0.0f, 0.3f,
1793 0.0f, 0.0f, 0.3f,
1794 0.0f, 0.0f, 0.3f,
1795 0.0f, 0.0f, 0.3f,
1796
1797 0.0f, 0.0f, 0.3f,
1798 0.0f, 0.0f, 0.3f,
1799 0.0f, 0.0f, 0.3f,
1800 0.0f, 0.0f, 0.3f,
1801 0.0f, 0.0f, 0.3f,
1802 0.0f, 0.0f, 0.3f,
1803
1804 0.0f, 0.0f, 0.3f,
1805 0.0f, 0.0f, 0.3f,
1806 0.0f, 0.0f, 0.3f,
1807
1808 0.0f, 0.0f, 0.3f,
1809 0.0f, 0.0f, 0.3f,
1810 0.0f, 0.0f, 0.3f,
1811
1812 0.0f, 0.0f, 0.3f,
1813 0.0f, 0.0f, 0.3f,
1814 0.0f, 0.0f, 0.3f,
1815
1816 0.0f, 0.0f, 0.3f,
1817 0.0f, 0.0f, 0.3f,
1818 0.0f, 0.0f, 0.3f,
1819 };
1820 ship->texcoords = { 0.0f };
1821
1822 mat4 T_model = translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f));
1823 mat4 R_model(1.0f);
1824 ship->model_base = T_model * R_model * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
1825
1826 ship->translate_mat = T_model;
1827
1828 initObject(ship);
1829
1830 return ship;
1831}
1832
1833/* LASER RENDERING/POSITIONING ALGORITHM
1834 * -Draw a thin rectangle for the laser beam, using the specified width and endpoints
1835 * -Texture the beam with a grayscale partially transparent image
1836 * -In the shader, blend the image with a color to support lasers of different colors
1837 *
1838 * The flat part of the textured rectangle needs to always face the camera, so the laser's width is constant
1839 * This is done as follows:
1840* -Determine the length of the laser based on the start and end points
1841* -Draw a rectangle along the z-axis and rotated upwards along the y-axis, with the correct final length and width
1842* -Rotate the beam around the z-axis by the correct angle, sot that in its final position, the flat part faces the camera
1843* -Rotate the beam along the x-axis and then along the y-axis and then translate it to put it into its final position
1844*/
1845// TODO: Make the color parameter have an effect
1846Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width) {
1847 Laser* obj = new Laser();
1848 obj->type = ObjectType::LASER;
1849 obj->targetAsteroid = NULL;
1850
1851 vec3 ray = end - start;
1852 float length = glm::length(ray);
1853
1854 obj->points = {
1855 width / 2, 0.0f, -width / 2,
1856 -width / 2, 0.0f, -width / 2,
1857 -width / 2, 0.0f, 0.0f,
1858 width / 2, 0.0f, -width / 2,
1859 -width / 2, 0.0f, 0.0f,
1860 width / 2, 0.0f, 0.0f,
1861 width / 2, 0.0f, -length + width / 2,
1862 -width / 2, 0.0f, -length + width / 2,
1863 -width / 2, 0.0f, -width / 2,
1864 width / 2, 0.0f, -length + width / 2,
1865 -width / 2, 0.0f, -width / 2,
1866 width / 2, 0.0f, -width / 2,
1867 width / 2, 0.0f, -length,
1868 -width / 2, 0.0f, -length,
1869 -width / 2, 0.0f, -length + width / 2,
1870 width / 2, 0.0f, -length,
1871 -width / 2, 0.0f, -length + width / 2,
1872 width / 2, 0.0f, -length + width / 2,
1873 };
1874
1875 obj->texcoords = {
1876 1.0f, 0.5f,
1877 0.0f, 0.5f,
1878 0.0f, 0.0f,
1879 1.0f, 0.5f,
1880 0.0f, 0.0f,
1881 1.0f, 0.0f,
1882 1.0f, 0.51f,
1883 0.0f, 0.51f,
1884 0.0f, 0.49f,
1885 1.0f, 0.51f,
1886 0.0f, 0.49f,
1887 1.0f, 0.49f,
1888 1.0f, 1.0f,
1889 0.0f, 1.0f,
1890 0.0f, 0.5f,
1891 1.0f, 1.0f,
1892 0.0f, 0.5f,
1893 1.0f, 0.5f,
1894 };
1895
1896 float xAxisRotation = asin(ray.y / length);
1897 float yAxisRotation = atan2(-ray.x, -ray.z);
1898
1899 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1900 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1901 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1902
1903 // To project point P onto line AB:
1904 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1905 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length * length) * ray;
1906 vec3 laserToCam = cam_pos - projOnLaser;
1907
1908 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1909
1910 obj->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1911
1912 initObject(obj);
1913
1914 obj->model_transform = rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) * obj->model_transform;
1915 obj->model_transform = rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) * obj->model_transform;
1916 obj->model_transform = translate(mat4(1.0f), start) * obj->model_transform;
1917
1918 return obj;
1919}
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
1944void defineModelGroupAttrib(ShaderModelGroup& modelGroup, string name, AttribType attribType,
1945 GLint size, GLenum type, size_t fieldOffset) {
1946 if (type != GL_FLOAT && type != GL_UNSIGNED_INT) {
1947 cout << "Unknown shader program attribute type: " << type << endl;
1948 return;
1949 }
1950
1951 AttribInfo attribInfo;
1952
1953 attribInfo.attribType = attribType;
1954 attribInfo.index = modelGroup.attribs.size();
1955 attribInfo.size = size;
1956 attribInfo.type = type;
1957 attribInfo.fieldOffset = fieldOffset;
1958
1959 modelGroup.attribs[name] = attribInfo;
1960}
1961
1962void defineModelGroupUniform(ShaderModelGroup& modelGroup, string name, AttribType attribType,
1963 GLint size, UniformType type, GLfloat* data) {
1964 AttribInfo attribInfo;
1965
1966 attribInfo.attribType = attribType;
1967 attribInfo.size = size;
1968 attribInfo.uniType = type;
1969 attribInfo.data = data;
1970
1971 modelGroup.attribs[name] = attribInfo;
1972}
1973
1974void initModelGroupAttribs(ShaderModelGroup& modelGroup) {
1975 glBindVertexArray(modelGroup.vao);
1976
1977 map<string, AttribInfo>::iterator it;
1978 for (it = modelGroup.attribs.begin(); it != modelGroup.attribs.end(); it++) {
1979 if (it->second.attribType == AttribType::UNIFORM) {
1980 it->second.buffer = glGetUniformLocation(modelGroup.shaderProgram, it->first.c_str());
1981 } else {
1982 glEnableVertexAttribArray(it->second.index);
1983
1984 glGenBuffers(1, &it->second.buffer);
1985 glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
1986
1987 switch (it->second.type) {
1988 case GL_FLOAT: {
1989 glVertexAttribPointer(it->second.index, it->second.size, it->second.type, GL_FALSE, 0, NULL);
1990 break;
1991 }
1992 case GL_UNSIGNED_INT: {
1993 glVertexAttribIPointer(it->second.index, it->second.size, it->second.type, 0, NULL);
1994 break;
1995 }
1996 }
1997 }
1998 }
1999}
2000
2001void bindUniformData(AttribInfo& attrib) {
2002 switch(attrib.uniType) {
2003 case UNIFORM_MATRIX_4F:
2004 glUniformMatrix4fv(attrib.buffer, attrib.size, GL_FALSE, attrib.data);
2005 break;
2006 case UNIFORM_1F:
2007 glUniform1fv(attrib.buffer, attrib.size, attrib.data);
2008 break;
2009 case UNIFORM_3F:
2010 glUniform3fv(attrib.buffer, attrib.size, attrib.data);
2011 break;
2012 case UNIFORM_NONE:
2013 break;
2014 }
2015}
2016
2017void bindUniformData(AttribInfo& attrib, GLfloat *data) {
2018 switch(attrib.uniType) {
2019 case UNIFORM_MATRIX_4F:
2020 glUniformMatrix4fv(attrib.buffer, attrib.size, GL_FALSE, data);
2021 break;
2022 case UNIFORM_1F:
2023 glUniform1fv(attrib.buffer, attrib.size, data);
2024 break;
2025 case UNIFORM_3F:
2026 glUniform3fv(attrib.buffer, attrib.size, data);
2027 break;
2028 case UNIFORM_NONE:
2029 break;
2030 }
2031}
2032
2033/* The purpose of this function is to replace the use of sizeof() when calling
2034 * function like glBufferSubData and using AttribInfo to get offsets and types
2035 * I need instead of hardcoding them. I can't save a type like GLfloat, but I cam
2036 * save GL_FLOAT and use this function to return sizeof(GLfloat) when GL_FLOAT is
2037 * passed.
2038 */
2039size_t GLsizeof(GLenum type) {
2040 switch (type) {
2041 case GL_FLOAT:
2042 return sizeof(GLfloat);
2043 case GL_UNSIGNED_INT:
2044 return sizeof(GLuint);
2045 default:
2046 cout << "Uknown GL type passed to GLsizeof: " << type << endl;
2047 return 0;
2048 }
2049}
2050
2051/* This function returns a reference to the first element of a given vector
2052 * attribute in obj. The vector is assumed to hold GLfloats. If the same thing
2053 * needs to be done later for vectors of other types, we could pass in a GLenum,
2054 * and do something similar to GLsizeof
2055 */
2056GLvoid* getVectorAttribPtr(SceneObject& obj, size_t attribOffset) {
2057 return (GLvoid*)(&(*(vector<GLfloat>*)((size_t)&obj + attribOffset))[0]);
2058}
2059
2060GLvoid* getScalarAttribPtr(SceneObject& obj, size_t attribOffset) {
2061 return (GLvoid*)((size_t)&obj + attribOffset);
2062}
2063
2064void populateBuffers(vector<SceneObject*>& objects,
2065 map<GLuint, BufferInfo>& shaderBufferInfo,
2066 map<ObjectType, ShaderModelGroup>& modelGroups,
2067 GLuint ubo) {
2068 GLsizeiptr num_points = 0;
2069 GLsizeiptr num_objects = 0;
2070
2071 map<GLuint, unsigned int> shaderCounts;
2072 map<GLuint, unsigned int> shaderUboCounts;
2073
2074 map<GLuint, BufferInfo>::iterator shaderIt;
2075
2076 for (shaderIt = shaderBufferInfo.begin(); shaderIt != shaderBufferInfo.end(); shaderIt++) {
2077 shaderCounts[shaderIt->first] = 0;
2078 shaderUboCounts[shaderIt->first] = 0;
2079 }
2080
2081 vector<SceneObject*>::iterator it;
2082
2083 /* Find all shaders that need to be used and the number of objects and
2084 * number of points for each shader. Construct a map from shader id to count
2085 * of points being drawn using that shader (for thw model matrix ubo, we
2086 * need object counts instead). These will be used to get offsets into the
2087 * vertex buffer for each shader.
2088 */
2089 for (it = objects.begin(); it != objects.end(); ) {
2090 if ((*it)->deleted) {
2091 delete *it;
2092 it = objects.erase(it);
2093 } else {
2094 num_points += (*it)->num_points;
2095 num_objects++;
2096
2097 shaderCounts[modelGroups[(*it)->type].shaderProgram] += (*it)->num_points;
2098 shaderUboCounts[modelGroups[(*it)->type].shaderProgram]++;
2099
2100 it++;
2101 }
2102 }
2103
2104 // double the buffer sizes to leave room for new objects
2105 num_points *= 2;
2106 num_objects *= 2;
2107
2108 map<GLuint, unsigned int>::iterator shaderCountIt;
2109 unsigned int lastShaderCount = 0;
2110 unsigned int lastShaderUboCount = 0;
2111
2112 /*
2113 * The counts calculated above can be used to get the starting offset of
2114 * each shader in the vertex buffer. Create a map of base offsets to mark
2115 * where the data for the first object using a given shader begins. Also,
2116 * create a map of current offsets to mark where to copy data for the next
2117 * object being added.
2118 */
2119 for (shaderCountIt = shaderCounts.begin(); shaderCountIt != shaderCounts.end(); shaderCountIt++) {
2120 // When populating the buffers, leave as much empty space as space taken up by existing objects
2121 // to allow new objects to be added without immediately having to resize the buffers
2122 shaderBufferInfo[shaderCountIt->first].ubo_base = lastShaderUboCount * 2;
2123
2124 shaderBufferInfo[shaderCountIt->first].ubo_offset = 0;
2125
2126 shaderBufferInfo[shaderCountIt->first].ubo_capacity = shaderUboCounts[shaderCountIt->first] * 2;
2127
2128 lastShaderCount += shaderCounts[shaderCountIt->first];
2129 lastShaderUboCount += shaderUboCounts[shaderCountIt->first];
2130 }
2131
2132 map<ObjectType, ShaderModelGroup>::iterator modelGroupIt;
2133 ShaderModelGroup* smg;
2134 for (modelGroupIt = modelGroups.begin(); modelGroupIt != modelGroups.end(); modelGroupIt++) {
2135 smg = &modelGroups[modelGroupIt->first];
2136
2137 smg->numPoints = 0;
2138 smg->vboCapacity = shaderCounts[smg->shaderProgram] * 2;
2139
2140 map<string, AttribInfo>::iterator attrIt;
2141 for (attrIt = smg->attribs.begin(); attrIt != smg->attribs.end(); attrIt++) {
2142 if (attrIt->second.attribType != AttribType::UNIFORM) {
2143 glBindBuffer(GL_ARRAY_BUFFER, attrIt->second.buffer);
2144 glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrIt->second.type) * attrIt->second.size, NULL, GL_DYNAMIC_DRAW);
2145 }
2146 }
2147 }
2148
2149 // Allocate the ubo using the counts calculated above
2150
2151 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2152 glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
2153
2154 for (it = objects.begin(); it != objects.end(); it++) {
2155 copyObjectDataToBuffers(**it, shaderBufferInfo, modelGroups, ubo);
2156 }
2157}
2158
2159void copyObjectDataToBuffers(SceneObject& obj,
2160 map<GLuint, BufferInfo>& shaderBufferInfo,
2161 map<ObjectType, ShaderModelGroup>& modelGroups,
2162 GLuint ubo) {
2163 BufferInfo* bufferInfo = &shaderBufferInfo[modelGroups[obj.type].shaderProgram];
2164
2165 obj.vertex_vbo_offset = modelGroups[obj.type].numPoints;
2166 obj.ubo_offset = bufferInfo->ubo_base + bufferInfo->ubo_offset;
2167
2168 ShaderModelGroup& smg = modelGroups[obj.type];
2169
2170 for (map<string, AttribInfo>::iterator it = smg.attribs.begin(); it != smg.attribs.end(); it++) {
2171 glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
2172
2173 switch (it->second.attribType) {
2174 case AttribType::POINT_VARYING:
2175 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * GLsizeof(it->second.type) * it->second.size,
2176 obj.num_points * GLsizeof(it->second.type) * it->second.size, getVectorAttribPtr(obj, it->second.fieldOffset));
2177 break;
2178 case AttribType::OBJECT_VARYING:
2179 for (unsigned int i = 0; i < obj.num_points; i++) {
2180 glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * GLsizeof(it->second.type) * it->second.size,
2181 GLsizeof(it->second.type) * it->second.size, getScalarAttribPtr(obj, it->second.fieldOffset));
2182 }
2183 break;
2184 case AttribType::UNIFORM:
2185 break;
2186 }
2187 }
2188
2189 obj.model_mat = obj.model_transform * obj.model_base;
2190
2191 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2192 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2193
2194 if (obj.type == ObjectType::ASTEROID) {
2195 glUseProgram(modelGroups[ObjectType::ASTEROID].shaderProgram);
2196
2197 ostringstream oss;
2198 oss << "hp[" << obj.ubo_offset << "]";
2199 glUniform1f(glGetUniformLocation(modelGroups[ObjectType::ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid&)obj).hp);
2200 } else if (obj.type == ObjectType::EXPLOSION) {
2201 glUseProgram(modelGroups[ObjectType::EXPLOSION].shaderProgram);
2202
2203 ostringstream oss;
2204 oss << "explosion_start_time[" << obj.ubo_offset << "]";
2205 glUniform1f(glGetUniformLocation(modelGroups[ObjectType::EXPLOSION].shaderProgram, oss.str().c_str()), ((ParticleEffect&)obj).startTime);
2206 }
2207
2208 modelGroups[obj.type].numPoints += obj.num_points;
2209 bufferInfo->ubo_offset++;
2210}
2211
2212void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo) {
2213 if (obj.deleted) return;
2214
2215 obj.model_transform = transform * obj.model_transform;
2216 obj.model_mat = obj.model_transform * obj.model_base;
2217
2218 obj.bounding_center = vec3(transform * vec4(obj.bounding_center, 1.0f));
2219
2220 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2221 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2222}
2223
2224void translateLaser(Laser* laser, const vec3& translation, GLuint ubo) {
2225 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2226 // and then re-used here
2227
2228 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2229 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[38], 1.0f));
2230
2231 vec3 ray = end - start;
2232 float length = glm::length(ray);
2233
2234 float xAxisRotation = asin(ray.y / length);
2235 float yAxisRotation = atan2(-ray.x, -ray.z);
2236
2237 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2238 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2239 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2240
2241 // To project point P onto line AB:
2242 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2243 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
2244 vec3 laserToCam = cam_pos - projOnLaser;
2245
2246 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2247
2248 laser->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2249
2250 transformObject(*laser, translate(mat4(1.0f), translation), ubo);
2251}
2252
2253void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, ShaderModelGroup& laserSmg, GLuint asteroid_sp) {
2254 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2255 // and then re-used here
2256
2257 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2258 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[2] + laser->points[20], 1.0f));
2259
2260 vec3 intersection(0.0f), closestIntersection(0.0f);
2261 Asteroid* closestAsteroid = NULL;
2262
2263 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
2264 if ((*it)->type == ObjectType::ASTEROID && !(*it)->deleted && getLaserAndAsteroidIntersection(start, end, **it, intersection)) {
2265 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2266 if (closestAsteroid == NULL || intersection.z > closestIntersection.z) {
2267 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2268 closestAsteroid = (Asteroid*)*it;
2269 closestIntersection = intersection;
2270 }
2271 }
2272 }
2273
2274 float width = laser->points[0] - laser->points[2];
2275
2276 if (laser->targetAsteroid != closestAsteroid) {
2277 if (laser->targetAsteroid != NULL) {
2278 if (laser == leftLaser) {
2279 leftLaserEffect->deleted = true;
2280 } else if (laser == rightLaser) {
2281 rightLaserEffect->deleted = true;
2282 }
2283 }
2284
2285 EffectOverTime* eot = NULL;
2286
2287 if (closestAsteroid != NULL) {
2288 eot = new EffectOverTime(closestAsteroid->hp, -20.0f, closestAsteroid);
2289 effects.push_back(eot);
2290 }
2291
2292 if (laser == leftLaser) {
2293 leftLaserEffect = eot;
2294 } else if (laser == rightLaser) {
2295 rightLaserEffect = eot;
2296 }
2297 }
2298 laser->targetAsteroid = closestAsteroid;
2299
2300 float length = 5.24f; // I think this was to make sure the laser went past the end of the screen
2301 if (closestAsteroid != NULL) {
2302 length = glm::length(closestIntersection - start);
2303
2304 // TODO: Find a more generic way of updating the asteroid hp than in updateLaserTarget
2305
2306 glUseProgram(asteroid_sp);
2307
2308 ostringstream oss;
2309 oss << "hp[" << closestAsteroid->ubo_offset << "]";
2310 glUniform1f(glGetUniformLocation(asteroid_sp, oss.str().c_str()), closestAsteroid->hp);
2311 }
2312
2313 laser->points[20] = -length + width / 2;
2314 laser->points[23] = -length + width / 2;
2315 laser->points[29] = -length + width / 2;
2316 laser->points[38] = -length;
2317 laser->points[41] = -length;
2318 laser->points[44] = -length + width / 2;
2319 laser->points[47] = -length;
2320 laser->points[50] = -length + width / 2;
2321 laser->points[53] = -length + width / 2;
2322
2323 AttribInfo* attrib = &laserSmg.attribs["vertex_position"];
2324 glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
2325 glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * GLsizeof(attrib->type) * attrib->size,
2326 laser->num_points * GLsizeof(attrib->type) * attrib->size, getVectorAttribPtr(*laser, attrib->fieldOffset));
2327}
2328
2329bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection) {
2330 /*
2331 ### LINE EQUATIONS ###
2332 x = x1 + u * (x2 - x1)
2333 y = y1 + u * (y2 - y1)
2334 z = z1 + u * (z2 - z1)
2335
2336 ### SPHERE EQUATION ###
2337 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2338
2339 ### QUADRATIC EQUATION TO SOLVE ###
2340 a*u^2 + b*u + c = 0
2341 WHERE THE CONSTANTS ARE
2342 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2343 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2344 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2345
2346 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2347
2348 If the value under the root is >= 0, we got an intersection
2349 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2350 one closer to the laser start point
2351 */
2352
2353 vec3& center = asteroid.bounding_center;
2354
2355 float a = pow(end.x-start.x, 2) + pow(end.y-start.y, 2) + pow(end.z-start.z, 2);
2356 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));
2357 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);
2358 float discriminant = pow(b, 2) - 4*a*c;
2359
2360 if (discriminant >= 0.0f) {
2361 // In this case, the negative root will always give the point closer to the laser start point
2362 float u = (-b - sqrt(discriminant)) / (2 * a);
2363
2364 // Check that the intersection is within the line segment corresponding to the laser
2365 if (0.0f <= u && u <= 1.0f) {
2366 intersection = start + u * (end - start);
2367 return true;
2368 }
2369 }
2370
2371 return false;
2372}
2373
2374void renderScene(map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo) {
2375
2376 glUseProgram(modelGroups[ObjectType::SHIP].shaderProgram);
2377 glBindVertexArray(modelGroups[ObjectType::SHIP].vao);
2378
2379 glDrawArrays(GL_TRIANGLES, 0, modelGroups[ObjectType::SHIP].numPoints);
2380
2381 glUseProgram(modelGroups[ObjectType::ASTEROID].shaderProgram);
2382 glBindVertexArray(modelGroups[ObjectType::ASTEROID].vao);
2383
2384 glDrawArrays(GL_TRIANGLES, 0, modelGroups[ObjectType::ASTEROID].numPoints);
2385
2386 glEnable(GL_BLEND);
2387
2388 glUseProgram(modelGroups[ObjectType::LASER].shaderProgram);
2389 glBindVertexArray(modelGroups[ObjectType::LASER].vao);
2390
2391 glDrawArrays(GL_TRIANGLES, 0, modelGroups[ObjectType::LASER].numPoints);
2392
2393 glUseProgram(modelGroups[ObjectType::EXPLOSION].shaderProgram);
2394 glBindVertexArray(modelGroups[ObjectType::EXPLOSION].vao);
2395
2396 glEnable(GL_PROGRAM_POINT_SIZE);
2397
2398 glDrawArrays(GL_POINTS, 0, modelGroups[ObjectType::EXPLOSION].numPoints);
2399
2400 glDisable(GL_PROGRAM_POINT_SIZE);
2401 glDisable(GL_BLEND);
2402}
2403/*** END OF REFACTORED CODE ***/
2404
2405void renderSceneGui(map<string, vector<UIValue>> valueLists) {
2406 ImGui_ImplOpenGL3_NewFrame();
2407 ImGui_ImplGlfw_NewFrame();
2408 ImGui::NewFrame();
2409
2410 // 1. Show a simple window.
2411 // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
2412 /*
2413 {
2414 static float f = 0.0f;
2415 static int counter = 0;
2416 ImGui::Text("Hello, world!"); // Display some text (you can use a format string too)
2417 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
2418
2419 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state
2420 ImGui::Checkbox("Another Window", &show_another_window);
2421
2422 if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated)
2423 counter++;
2424 ImGui::SameLine();
2425 ImGui::Text("counter = %d", counter);
2426
2427 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
2428 }
2429 */
2430
2431 {
2432 ImGui::SetNextWindowSize(ImVec2(95, 46), ImGuiCond_Once);
2433 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
2434 ImGui::Begin("WndStats", NULL,
2435 ImGuiWindowFlags_NoTitleBar |
2436 ImGuiWindowFlags_NoResize |
2437 ImGuiWindowFlags_NoMove);
2438
2439 renderGuiValueList(valueLists["stats value list"]);
2440
2441 ImGui::End();
2442 }
2443
2444 {
2445 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
2446 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
2447 ImGui::Begin("WndMenubar", NULL,
2448 ImGuiWindowFlags_NoTitleBar |
2449 ImGuiWindowFlags_NoResize |
2450 ImGuiWindowFlags_NoMove);
2451 ImGui::InvisibleButton("", ImVec2(155, 18));
2452 ImGui::SameLine();
2453 if (ImGui::Button("Main Menu")) {
2454 events.push(Event::GO_TO_MAIN_MENU);
2455 }
2456 ImGui::End();
2457 }
2458
2459 {
2460 ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
2461 ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
2462 ImGui::Begin("WndDebug", NULL,
2463 ImGuiWindowFlags_NoTitleBar |
2464 ImGuiWindowFlags_NoResize |
2465 ImGuiWindowFlags_NoMove);
2466
2467 renderGuiValueList(valueLists["debug value list"]);
2468
2469 ImGui::End();
2470 }
2471
2472 ImGui::Render();
2473 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
2474}
2475
2476void renderMainMenu() {
2477}
2478
2479/*** START OF REFACTORED CODE ***/
2480void renderMainMenuGui() {
2481 ImGui_ImplOpenGL3_NewFrame();
2482 ImGui_ImplGlfw_NewFrame();
2483 ImGui::NewFrame();
2484
2485 {
2486 int padding = 4;
2487 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
2488 ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
2489 ImGui::Begin("WndMain", NULL,
2490 ImGuiWindowFlags_NoTitleBar |
2491 ImGuiWindowFlags_NoResize |
2492 ImGuiWindowFlags_NoMove);
2493
2494 ImGui::InvisibleButton("", ImVec2(10, 80));
2495 ImGui::InvisibleButton("", ImVec2(285, 18));
2496 ImGui::SameLine();
2497 if (ImGui::Button("New Game")) {
2498 events.push(Event::GO_TO_GAME);
2499 }
2500
2501 ImGui::InvisibleButton("", ImVec2(10, 15));
2502 ImGui::InvisibleButton("", ImVec2(300, 18));
2503 ImGui::SameLine();
2504 if (ImGui::Button("Quit")) {
2505 events.push(Event::QUIT);
2506 }
2507
2508 ImGui::End();
2509 }
2510
2511 ImGui::Render();
2512 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
2513}
2514/*** END OF REFACTORED CODE ***/
2515
2516void initGuiValueLists(map<string, vector<UIValue>> valueLists) {
2517 valueLists["stats value list"] = vector<UIValue>();
2518 valueLists["debug value list"] = vector<UIValue>();
2519}
2520
2521void renderGuiValueList(vector<UIValue>& values) {
2522 float maxWidth = 0.0f;
2523 float cursorStartPos = ImGui::GetCursorPosX();
2524
2525 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2526 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2527
2528 if (maxWidth < textWidth)
2529 maxWidth = textWidth;
2530 }
2531
2532 stringstream ss;
2533
2534 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2535 ss.str("");
2536 ss.clear();
2537
2538 switch (it->type) {
2539 case UIVALUE_INT:
2540 ss << it->label << ": " << *(unsigned int*)it->value;
2541 break;
2542 case UIVALUE_DOUBLE:
2543 ss << it->label << ": " << *(double*)it->value;
2544 break;
2545 }
2546
2547 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2548
2549 ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
2550 ImGui::Text("%s", ss.str().c_str());
2551 }
2552}
2553
2554/*** START OF REFACTORED CODE ***/
2555Asteroid* createAsteroid(vec3 pos) {
2556 Asteroid* obj = new Asteroid();
2557 obj->type = ObjectType::ASTEROID;
2558 obj->hp = 10.0f;
2559
2560 obj->points = {
2561 // front
2562 1.0f, 1.0f, 1.0f,
2563 -1.0f, 1.0f, 1.0f,
2564 -1.0f, -1.0f, 1.0f,
2565 1.0f, 1.0f, 1.0f,
2566 -1.0f, -1.0f, 1.0f,
2567 1.0f, -1.0f, 1.0f,
2568
2569 // top
2570 1.0f, 1.0f, -1.0f,
2571 -1.0f, 1.0f, -1.0f,
2572 -1.0f, 1.0f, 1.0f,
2573 1.0f, 1.0f, -1.0f,
2574 -1.0f, 1.0f, 1.0f,
2575 1.0f, 1.0f, 1.0f,
2576
2577 // bottom
2578 1.0f, -1.0f, 1.0f,
2579 -1.0f, -1.0f, 1.0f,
2580 -1.0f, -1.0f, -1.0f,
2581 1.0f, -1.0f, 1.0f,
2582 -1.0f, -1.0f, -1.0f,
2583 1.0f, -1.0f, -1.0f,
2584
2585 // back
2586 1.0f, 1.0f, -1.0f,
2587 -1.0f, -1.0f, -1.0f,
2588 -1.0f, 1.0f, -1.0f,
2589 1.0f, 1.0f, -1.0f,
2590 1.0f, -1.0f, -1.0f,
2591 -1.0f, -1.0f, -1.0f,
2592
2593 // right
2594 1.0f, 1.0f, -1.0f,
2595 1.0f, 1.0f, 1.0f,
2596 1.0f, -1.0f, 1.0f,
2597 1.0f, 1.0f, -1.0f,
2598 1.0f, -1.0f, 1.0f,
2599 1.0f, -1.0f, -1.0f,
2600
2601 // left
2602 -1.0f, 1.0f, 1.0f,
2603 -1.0f, 1.0f, -1.0f,
2604 -1.0f, -1.0f, -1.0f,
2605 -1.0f, 1.0f, 1.0f,
2606 -1.0f, -1.0f, -1.0f,
2607 -1.0f, -1.0f, 1.0f,
2608 };
2609 obj->colors = {
2610 // front
2611 0.4f, 0.4f, 0.4f,
2612 0.4f, 0.4f, 0.4f,
2613 0.4f, 0.4f, 0.4f,
2614 0.4f, 0.4f, 0.4f,
2615 0.4f, 0.4f, 0.4f,
2616 0.4f, 0.4f, 0.4f,
2617
2618 // top
2619 0.4f, 0.4f, 0.4f,
2620 0.4f, 0.4f, 0.4f,
2621 0.4f, 0.4f, 0.4f,
2622 0.4f, 0.4f, 0.4f,
2623 0.4f, 0.4f, 0.4f,
2624 0.4f, 0.4f, 0.4f,
2625
2626 // bottom
2627 0.4f, 0.4f, 0.4f,
2628 0.4f, 0.4f, 0.4f,
2629 0.4f, 0.4f, 0.4f,
2630 0.4f, 0.4f, 0.4f,
2631 0.4f, 0.4f, 0.4f,
2632 0.4f, 0.4f, 0.4f,
2633
2634 // back
2635 0.4f, 0.4f, 0.4f,
2636 0.4f, 0.4f, 0.4f,
2637 0.4f, 0.4f, 0.4f,
2638 0.4f, 0.4f, 0.4f,
2639 0.4f, 0.4f, 0.4f,
2640 0.4f, 0.4f, 0.4f,
2641
2642 // right
2643 0.4f, 0.4f, 0.4f,
2644 0.4f, 0.4f, 0.4f,
2645 0.4f, 0.4f, 0.4f,
2646 0.4f, 0.4f, 0.4f,
2647 0.4f, 0.4f, 0.4f,
2648 0.4f, 0.4f, 0.4f,
2649
2650 // left
2651 0.4f, 0.4f, 0.4f,
2652 0.4f, 0.4f, 0.4f,
2653 0.4f, 0.4f, 0.4f,
2654 0.4f, 0.4f, 0.4f,
2655 0.4f, 0.4f, 0.4f,
2656 0.4f, 0.4f, 0.4f,
2657 };
2658 obj->texcoords = { 0.0f };
2659
2660 mat4 T = translate(mat4(1.0f), pos);
2661 mat4 R = rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f));
2662 obj->model_base = T * R * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
2663
2664 obj->translate_mat = T;
2665
2666 initObject(obj);
2667 // This accounts for the scaling in model_base.
2668 // Dividing by 8 instead of 10 since the bounding radius algorithm
2669 // under-calculates the true value.
2670 // TODO: Once the intersection check with the sides of the asteroid is done,
2671 // this can be removed.
2672 obj->bounding_radius /= 8.0f;
2673
2674 return obj;
2675}
2676
2677// TODO: Maybe pass in startTime instead of calling glfwGetTime() here
2678ParticleEffect* createExplosion(mat4 model_mat) {
2679 ParticleEffect* obj = new ParticleEffect();
2680
2681 obj->type = ObjectType::EXPLOSION;
2682
2683 initObject(obj);
2684
2685 obj->num_points = EXPLOSION_PARTICLE_COUNT;
2686 obj->model_base = model_mat;
2687 obj->startTime = glfwGetTime();
2688 obj->duration = 0.5f; // This is also hard-coded in the shader. TODO; Pass this to the shader in an indexable ubo.
2689
2690 obj->particleVelocities.clear();
2691 obj->particleTimes.clear();
2692 float t_accum = 0.0f; // start time
2693
2694 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
2695 obj->particleTimes.push_back(t_accum);
2696 t_accum += 0.01f;
2697
2698 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
2699 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
2700 obj->particleVelocities.push_back(randx);
2701 obj->particleVelocities.push_back(randy);
2702 obj->particleVelocities.push_back(0.0f);
2703 }
2704
2705 return obj;
2706}
2707/*** END OF REFACTORED CODE ***/
Note: See TracBrowser for help on using the repository browser.