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

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

Add renderScene and renderUI functions to openglgame and use IMGUI to render the main menu

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