source: opengl-game/new-game.cpp@ 4c7cd57

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

Use a ShaderModelGroup for the ship

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