source: opengl-game/new-game.cpp@ 646f3f2

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

Make explosions render correctly whenever a ship is destroyed.

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