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

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

WIP

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