source: opengl-game/new-game.cpp@ 3de31cf

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

Move the OpenGL shaders to a gl-shaders folder

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