source: opengl-game/new-game.cpp@ 39ac76d

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

Remove some unused code and rename some variables to more descriptive names

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