source: opengl-game/new-game.cpp@ 845a2cb

points-test
Last change on this file since 845a2cb was 845a2cb, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

test point size

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