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

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

Create a map of ShaderModelGroups and use it for asteroids and explosions

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