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

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

WIP continued

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