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

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

Remove SceneObject.shader_program and use the object type to get the shader program from the ShaderModelGroup map instead

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