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

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

Use a ShaderModelGroup for the lasers amd remove BufferInfo.vbo_offset since it's no longer being used

  • Property mode set to 100644
File size: 83.4 KB
Line 
1#include "logger.h"
2
3#include "stb_image.h"
4
5// I think this was for the OpenGL 4 book font file tutorial
6//#define STB_IMAGE_WRITE_IMPLEMENTATION
7//#include "stb_image_write.h"
8
9#define _USE_MATH_DEFINES
10
11#include <glm/mat4x4.hpp>
12#include <glm/gtc/matrix_transform.hpp>
13#include <glm/gtc/type_ptr.hpp>
14
15#include "IMGUI/imgui.h"
16#include "imgui_impl_glfw_gl3.h"
17
18#include <GL/glew.h>
19#include <GLFW/glfw3.h>
20
21#include <cstdio>
22#include <cstdlib>
23#include <ctime>
24#include <iostream>
25#include <fstream>
26#include <sstream>
27#include <cmath>
28#include <string>
29#include <array>
30#include <vector>
31#include <queue>
32#include <map>
33
34using namespace std;
35using namespace glm;
36
37enum State {
38 STATE_MAIN_MENU,
39 STATE_GAME,
40};
41
42enum Event {
43 EVENT_GO_TO_MAIN_MENU,
44 EVENT_GO_TO_GAME,
45 EVENT_QUIT,
46};
47
48enum ObjectType {
49 TYPE_SHIP,
50 TYPE_ASTEROID,
51 TYPE_LASER,
52 TYPE_EXPLOSION,
53};
54
55// TODO: Once all object types use ShaderModelGroup objects,, I should be able to remove the
56// shader_program field since it would be available via modelGroups[type].shaderProgram
57struct SceneObject {
58 unsigned int id;
59 ObjectType type;
60
61 // Currently, model_transform should only have translate, and rotation and scale need to be done in model_base since
62 // they need to be done when the object is at the origin. I should change this to have separate scale, rotate, and translate
63 // matrices for each object that can be updated independently and then applied to the object in that order.
64 // TODO: Actually, to make this as generic as possible, each object should have a matrix stack to support,
65 // for instance, applying a rotate, then a translate, then another rotate. Think about and implement the best approach.
66 mat4 model_mat, model_base, model_transform;
67 mat4 translate_mat; // beginning of doing what's mentioned above
68 GLuint shader_program;
69 unsigned int num_points;
70 GLuint vertex_vbo_offset;
71 GLuint ubo_offset;
72 vector<GLfloat> points;
73 vector<GLfloat> colors;
74 vector<GLfloat> texcoords;
75 vector<GLfloat> normals;
76 bool deleted;
77 vec3 bounding_center;
78 GLfloat bounding_radius;
79};
80
81struct Asteroid : SceneObject {
82 float hp;
83};
84
85struct Laser : SceneObject {
86 Asteroid* targetAsteroid;
87};
88
89struct EffectOverTime {
90 float& effectedValue;
91 float startValue;
92 double startTime;
93 float changePerSecond;
94 bool deleted;
95 SceneObject* effectedObject;
96
97 // TODO: Why not just use an initializer list for all the instance variables
98 EffectOverTime(float& effectedValue, float changePerSecond, SceneObject* object)
99 : effectedValue(effectedValue), changePerSecond(changePerSecond), effectedObject(object) {
100 startValue = effectedValue;
101 startTime = glfwGetTime();
102 deleted = false;
103 }
104};
105
106struct BufferInfo {
107 unsigned int vbo_base;
108 unsigned int vbo_capacity;
109 unsigned int ubo_base;
110 unsigned int ubo_offset;
111 unsigned int ubo_capacity;
112};
113
114struct ShaderModelGroup {
115 GLuint shaderProgram;
116 GLuint vao;
117 unsigned int numPoints;
118};
119
120void glfw_error_callback(int error, const char* description);
121
122void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
123void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
124
125void APIENTRY debugGlCallback(
126 GLenum source,
127 GLenum type,
128 GLuint id,
129 GLenum severity,
130 GLsizei length,
131 const GLchar* message,
132 const void* userParam
133);
134
135bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point);
136bool insideTriangle(vec3 p, array<vec3, 3> triangle_points);
137
138GLuint loadShader(GLenum type, string file);
139GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath);
140unsigned char* loadImage(string file_name, int* x, int* y);
141
142void printVector(string label, vec3& v);
143void print4DVector(string label, vec4& v);
144
145void initObject(SceneObject* obj);
146void addObjectToScene(SceneObject* obj,
147 map<GLuint, BufferInfo>& shaderBufferInfo,
148 map<ObjectType, ShaderModelGroup>& modelGroups,
149 GLuint points_vbo,
150 GLuint colors_vbo,
151 GLuint texcoords_vbo,
152 GLuint normals_vbo,
153 GLuint ubo,
154 GLuint model_mat_idx_vbo);
155void removeObjectFromScene(SceneObject& obj, GLuint ubo);
156
157ShaderModelGroup createModelGroup(GLuint shaderProgram);
158void removeModelFromGroup(ShaderModelGroup& modelGroup, SceneObject& model);
159void addModelToGroup(ShaderModelGroup& modelGroup, SceneObject& model);
160
161void calculateObjectBoundingBox(SceneObject* obj);
162
163void initializeBuffers(
164 GLuint* points_vbo,
165 GLuint* colors_vbo,
166 GLuint* texcoords_vbo,
167 GLuint* normals_vbo,
168 GLuint* ubo,
169 GLuint* model_mat_idx_vbo);
170
171void initializeParticleEffectBuffers(vec3 origin, mat4 proj, mat4 view,
172 map<GLuint, BufferInfo>& shaderBufferInfo,
173 map<ObjectType, ShaderModelGroup>& modelGroups,
174 GLuint points_vbo,
175 GLuint colors_vbo,
176 GLuint texcoords_vbo,
177 GLuint normals_vbo,
178 GLuint ubo,
179 GLuint model_mat_idx_vbo);
180
181void populateBuffers(vector<SceneObject*>& objects,
182 map<GLuint, BufferInfo>& shaderBufferInfo,
183 map<ObjectType, ShaderModelGroup>& modelGroups,
184 GLuint points_vbo,
185 GLuint colors_vbo,
186 GLuint texcoords_vbo,
187 GLuint normals_vbo,
188 GLuint ubo,
189 GLuint model_mat_idx_vbo);
190
191void copyObjectDataToBuffers(SceneObject& obj,
192 map<GLuint, BufferInfo>& shaderBufferInfo,
193 map<ObjectType, ShaderModelGroup>& modelGroups,
194 GLuint points_vbo,
195 GLuint colors_vbo,
196 GLuint texcoords_vbo,
197 GLuint normals_vbo,
198 GLuint ubo,
199 GLuint model_mat_idx_vbo);
200
201void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo);
202
203// TODO: instead of using these methods, create constructors for these
204SceneObject* createShip(GLuint shader);
205Asteroid* createAsteroid(vec3 pos, GLuint shader);
206Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width, GLuint laser_sp);
207SceneObject* createExplosion(GLuint shader);
208
209void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
210void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp);
211bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection);
212
213void renderMainMenu();
214void renderMainMenuGui();
215
216void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
217 map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo);
218
219void renderSceneGui();
220
221float getRandomNum(float low, float high);
222
223#define NUM_KEYS (512)
224#define ONE_DEG_IN_RAD ((2.0f * M_PI) / 360.0f) // 0.017444444 (maybe make this a const instead)
225#define TARGET_FPS 60.0f
226
227const int KEY_STATE_UNCHANGED = -1;
228const bool FULLSCREEN = false;
229const int EXPLOSION_PARTICLE_COUNT = 300;
230unsigned int MAX_UNIFORMS = 0; // Requires OpenGL constants only available at runtime, so it can't be const
231
232int key_state[NUM_KEYS];
233bool key_down[NUM_KEYS];
234
235int width = 640;
236int height = 480;
237
238double fps;
239unsigned int score = 0;
240
241vec3 cam_pos;
242
243mat4 view_mat;
244mat4 proj_mat;
245
246vector<SceneObject*> objects;
247queue<Event> events;
248vector<EffectOverTime*> effects;
249
250SceneObject* clickedObject = NULL;
251SceneObject* selectedObject = NULL;
252
253float NEAR_CLIP = 0.1f;
254float FAR_CLIP = 100.0f;
255
256// TODO: Should really have some array or struct of UI-related variables
257bool isRunning = true;
258
259ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
260
261Laser* leftLaser = NULL;
262EffectOverTime* leftLaserEffect = NULL;
263
264Laser* rightLaser = NULL;
265EffectOverTime* rightLaserEffect = NULL;
266
267SceneObject* objExplosion;
268SceneObject* objFirst;
269
270/*
271* TODO: Asteroid and ship movement currently depend on framerate, fix this in a generic/reusable way
272* Disabling vsync is a great way to test this
273*/
274
275int main(int argc, char* argv[]) {
276 cout << "New OpenGL Game" << endl;
277
278 if (!restart_gl_log()) {}
279 gl_log("starting GLFW\n%s\n", glfwGetVersionString());
280
281 glfwSetErrorCallback(glfw_error_callback);
282 if (!glfwInit()) {
283 fprintf(stderr, "ERROR: could not start GLFW3\n");
284 return 1;
285 }
286
287#ifdef __APPLE__
288 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
289 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
290 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
291 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
292#else
293 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
294 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
295#endif
296
297 GLFWwindow* window = NULL;
298 GLFWmonitor* mon = NULL;
299
300 glfwWindowHint(GLFW_SAMPLES, 16);
301 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
302
303 if (FULLSCREEN) {
304 mon = glfwGetPrimaryMonitor();
305 const GLFWvidmode* vmode = glfwGetVideoMode(mon);
306
307 width = vmode->width;
308 height = vmode->height;
309 cout << "Fullscreen resolution " << vmode->width << "x" << vmode->height << endl;
310 }
311 window = glfwCreateWindow(width, height, "New OpenGL Game", mon, NULL);
312
313 if (!window) {
314 fprintf(stderr, "ERROR: could not open window with GLFW3\n");
315 glfwTerminate();
316 return 1;
317 }
318
319 glfwMakeContextCurrent(window);
320 glewExperimental = GL_TRUE;
321 glewInit();
322
323 if (GLEW_KHR_debug) {
324 cout << "FOUND GLEW debug extension" << endl;
325 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
326 glDebugMessageCallback((GLDEBUGPROC)debugGlCallback, nullptr);
327 cout << "Bound debug callback" << endl;
328 } else {
329 cout << "OpenGL debugg message callback is not supported" << endl;
330 }
331
332 srand(time(0));
333
334 /*
335 * RENDERING ALGORITHM NOTES:
336 *
337 * Basically, I need to split my objects into groups, so that each group fits into
338 * GL_MAX_UNIFORM_BLOCK_SIZE. I need to have an offset and a size for each group.
339 * Getting the offset is straitforward. The size may as well be GL_MAX_UNIFORM_BLOCK_SIZE
340 * for each group, since it seems that smaller sizes just round up to the nearest GL_MAX_UNIFORM_BLOCK_SIZE
341 *
342 * I'll need to have a loop inside my render loop that calls glBindBufferRange(GL_UNIFORM_BUFFER, ...
343 * for every 1024 objects and then draws all those objects with one glDraw call.
344 *
345 * Since I currently have very few objects, I'll wait to implement this until I have
346 * a reasonable number of objects always using the same shader.
347 */
348
349 GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT, MAX_UNIFORM_BLOCK_SIZE;
350 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &UNIFORM_BUFFER_OFFSET_ALIGNMENT);
351 glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &MAX_UNIFORM_BLOCK_SIZE);
352
353 MAX_UNIFORMS = MAX_UNIFORM_BLOCK_SIZE / sizeof(mat4);
354
355 cout << "UNIFORM_BUFFER_OFFSET_ALIGNMENT: " << UNIFORM_BUFFER_OFFSET_ALIGNMENT << endl;
356 cout << "MAX_UNIFORMS: " << MAX_UNIFORMS << endl;
357
358 // Setup Dear ImGui binding
359 IMGUI_CHECKVERSION();
360 ImGui::CreateContext();
361 ImGuiIO& io = ImGui::GetIO(); (void)io;
362 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
363 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
364 ImGui_ImplGlfwGL3_Init(window, true);
365
366 // Setup style
367 ImGui::StyleColorsDark();
368 //ImGui::StyleColorsClassic();
369
370 glfwSetMouseButtonCallback(window, mouse_button_callback);
371 glfwSetKeyCallback(window, key_callback);
372
373 const GLubyte* renderer = glGetString(GL_RENDERER);
374 const GLubyte* version = glGetString(GL_VERSION);
375 cout << "Renderer: " << renderer << endl;
376 cout << "OpenGL version supported " << version << endl;
377
378 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
379
380 glEnable(GL_DEPTH_TEST);
381 glDepthFunc(GL_LESS);
382
383 glEnable(GL_CULL_FACE);
384 // glCullFace(GL_BACK);
385 // glFrontFace(GL_CW);
386
387 /*
388 int x, y;
389 unsigned char* texImage = loadImage("test.png", &x, &y);
390 if (texImage) {
391 cout << "Yay, I loaded an image!" << endl;
392 cout << x << endl;
393 cout << y << endl;
394 printf("first 4 bytes are: %i %i %i %i\n", texImage[0], texImage[1], texImage[2], texImage[3]);
395 }
396
397 GLuint testTex = 0;
398 glGenTextures(1, &testTex);
399 glActiveTexture(GL_TEXTURE0);
400 glBindTexture(GL_TEXTURE_2D, testTex);
401 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
402
403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
404 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
405 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
406 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
407 */
408
409 int x, y;
410 unsigned char* texImage = loadImage("laser.png", &x, &y);
411 if (texImage) {
412 cout << "Laser texture loaded successfully!" << endl;
413 cout << x << endl;
414 cout << y << endl;
415 printf("first 4 bytes are: %i %i %i %i\n", texImage[0], texImage[1], texImage[2], texImage[3]);
416 }
417
418 GLuint laserTex = 0;
419 glGenTextures(1, &laserTex);
420 glActiveTexture(GL_TEXTURE0);
421 glBindTexture(GL_TEXTURE_2D, laserTex);
422 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
423
424 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
425 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
426 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
427 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
428
429 /* RENDERING ALGORITHM
430 *
431 * Create a separate vbo for each of the following things:
432 * - points
433 * - colors
434 * - texture coordinates
435 * - selected colors
436 * - normals
437 * - indices into a ubo that stores a model matrix for each object
438 *
439 * Also, make a model matrix ubo, the entirety of which will be passed to the vertex shader.
440 * The vbo containing the correct index into the ubo (mentioned above) will be used to select
441 * the right model matrix for each point. The index in the vbo will be the saem for all points
442 * of any given object.
443 *
444 * There will be two shader programs for now, one for draing colored objects, and another for
445 * drawing textured ones. The points, normals, and model mat ubo indices will be passed to both
446 * shaders, while the colors vbo will only be passed to the colors shader, and the texcoords vbo
447 * only to the texture shader.
448 *
449 * Right now, the currently selected object is drawn using one color (specified in the selected
450 * colors vbo) regardless of whether it is normally rendering using colors or a texture. The selected
451 * object is rendering by binding the selected colors vbo in place of the colors vbo and using the colors
452 * shader. Then, the selected object is redrawn along with all other objects, but the depth buffer test
453 * prevents the unselected version of the object from appearing on the screen. This lets me render all the
454 * objects that use a particular shader using one glDrawArrays() call.
455 */
456
457 map<GLuint, BufferInfo> shaderBufferInfo;
458 map<ObjectType, ShaderModelGroup> modelGroups;
459
460 modelGroups[TYPE_SHIP] = createModelGroup(
461 loadShaderProgram("./ship.vert", "./ship.frag"));
462 shaderBufferInfo[modelGroups[TYPE_SHIP].shaderProgram] = BufferInfo(); // temporary
463
464 modelGroups[TYPE_ASTEROID] = createModelGroup(
465 loadShaderProgram("./asteroid.vert", "./asteroid.frag"));
466 shaderBufferInfo[modelGroups[TYPE_ASTEROID].shaderProgram] = BufferInfo(); // temporary
467
468 modelGroups[TYPE_LASER] = createModelGroup(
469 loadShaderProgram("./laser.vert", "./laser.frag"));
470 shaderBufferInfo[modelGroups[TYPE_LASER].shaderProgram] = BufferInfo(); // temporary
471
472 modelGroups[TYPE_EXPLOSION] = createModelGroup(
473 loadShaderProgram("./explosion.vert", "./explosion.frag"));
474 shaderBufferInfo[modelGroups[TYPE_EXPLOSION].shaderProgram] = BufferInfo(); // temporary
475
476 cam_pos = vec3(0.0f, 0.0f, 2.0f);
477 float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
478 float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
479
480 // player ship
481 SceneObject* ship = createShip(modelGroups[TYPE_SHIP].shaderProgram);
482 objects.push_back(ship);
483
484 vector<SceneObject>::iterator obj_it;
485
486 GLuint points_vbo, colors_vbo, texcoords_vbo, normals_vbo, ubo, model_mat_idx_vbo;
487
488 initializeBuffers(
489 &points_vbo,
490 &colors_vbo,
491 &texcoords_vbo,
492 &normals_vbo,
493 &ubo,
494 &model_mat_idx_vbo);
495
496 populateBuffers(objects,
497 shaderBufferInfo, modelGroups,
498 points_vbo,
499 colors_vbo,
500 texcoords_vbo,
501 normals_vbo,
502 ubo,
503 model_mat_idx_vbo);
504
505 glBindVertexArray(modelGroups[TYPE_SHIP].vao);
506
507 glEnableVertexAttribArray(0);
508 glEnableVertexAttribArray(1);
509 glEnableVertexAttribArray(2);
510 glEnableVertexAttribArray(3);
511
512 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
513 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
514
515 // Comment these two lines out when I want to use selected colors
516 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
517 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
518
519 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
520 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
521
522 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
523 glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
524
525 glBindVertexArray(modelGroups[TYPE_ASTEROID].vao);
526
527 glEnableVertexAttribArray(0);
528 glEnableVertexAttribArray(1);
529 glEnableVertexAttribArray(2);
530 glEnableVertexAttribArray(3);
531
532 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
533 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
534
535 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
536 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
537
538 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
539 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
540
541 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
542 glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
543
544 glBindVertexArray(modelGroups[TYPE_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(modelGroups[TYPE_SHIP].shaderProgram, "view");
638 GLuint ship_proj_mat_loc = glGetUniformLocation(modelGroups[TYPE_SHIP].shaderProgram, "proj");
639 GLuint ship_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_SHIP].shaderProgram, "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(modelGroups[TYPE_LASER].shaderProgram, "view");
646 GLuint laser_proj_mat_loc = glGetUniformLocation(modelGroups[TYPE_LASER].shaderProgram, "proj");
647 GLuint laser_color_loc = glGetUniformLocation(modelGroups[TYPE_LASER].shaderProgram, "laser_color");
648 GLuint laser_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_LASER].shaderProgram, "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(modelGroups[TYPE_SHIP].shaderProgram);
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(modelGroups[TYPE_SHIP].shaderProgram, 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(modelGroups[TYPE_LASER].shaderProgram);
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(modelGroups[TYPE_LASER].shaderProgram, 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, modelGroups[TYPE_LASER].shaderProgram);
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, modelGroups[TYPE_LASER].shaderProgram);
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(modelGroups[TYPE_SHIP].shaderProgram);
949 glUniformMatrix4fv(ship_view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
950
951 glUseProgram(modelGroups[TYPE_LASER].shaderProgram);
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 renderSceneGui();
972 break;
973 }
974
975 glfwSwapBuffers(window);
976 }
977
978 ImGui_ImplGlfwGL3_Shutdown();
979 ImGui::DestroyContext();
980
981 glfwDestroyWindow(window);
982 glfwTerminate();
983
984 // free memory
985
986 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
987 delete *it;
988 }
989
990 return 0;
991}
992
993void glfw_error_callback(int error, const char* description) {
994 gl_log_err("GLFW ERROR: code %i msg: %s\n", error, description);
995}
996
997void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
998 double mouse_x, mouse_y;
999 glfwGetCursorPos(window, &mouse_x, &mouse_y);
1000
1001 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
1002 cout << "Mouse clicked (" << mouse_x << "," << mouse_y << ")" << endl;
1003 selectedObject = NULL;
1004
1005 float x = (2.0f*mouse_x) / width - 1.0f;
1006 float y = 1.0f - (2.0f*mouse_y) / height;
1007
1008 cout << "x: " << x << ", y: " << y << endl;
1009
1010 vec4 ray_clip = vec4(x, y, -1.0f, 1.0f);
1011 vec4 ray_eye = inverse(proj_mat) * ray_clip;
1012 ray_eye = vec4(vec2(ray_eye), -1.0f, 1.0f);
1013 vec4 ray_world = inverse(view_mat) * ray_eye;
1014
1015 vec4 click_point;
1016 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
1017 SceneObject* closest_object = NULL;
1018
1019 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
1020 if ((*it)->type == TYPE_LASER) continue;
1021 for (unsigned int p_idx = 0; p_idx < (*it)->points.size(); p_idx += 9) {
1022 if (faceClicked(
1023 {
1024 vec3((*it)->points[p_idx], (*it)->points[p_idx + 1], (*it)->points[p_idx + 2]),
1025 vec3((*it)->points[p_idx + 3], (*it)->points[p_idx + 4], (*it)->points[p_idx + 5]),
1026 vec3((*it)->points[p_idx + 6], (*it)->points[p_idx + 7], (*it)->points[p_idx + 8]),
1027 },
1028 *it, ray_world, vec4(cam_pos, 1.0f), click_point
1029 )) {
1030 click_point = view_mat * click_point;
1031
1032 if (-NEAR_CLIP >= click_point.z && click_point.z > -FAR_CLIP && click_point.z > closest_point.z) {
1033 closest_point = vec3(click_point);
1034 closest_object = *it;
1035 }
1036 }
1037 }
1038 }
1039
1040 if (closest_object == NULL) {
1041 cout << "No object was clicked" << endl;
1042 } else {
1043 clickedObject = closest_object;
1044 cout << "Clicked object: " << clickedObject->id << endl;
1045 }
1046 }
1047}
1048
1049void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
1050 key_state[key] = action;
1051
1052 // should be true for GLFW_PRESS and GLFW_REPEAT
1053 key_down[key] = (action != GLFW_RELEASE);
1054}
1055
1056void APIENTRY debugGlCallback(
1057 GLenum source,
1058 GLenum type,
1059 GLuint id,
1060 GLenum severity,
1061 GLsizei length,
1062 const GLchar* message,
1063 const void* userParam
1064) {
1065 string strMessage(message);
1066
1067 // TODO: Use C++ strings directly
1068 char source_str[2048];
1069 char type_str[2048];
1070 char severity_str[2048];
1071
1072 switch (source) {
1073 case 0x8246:
1074 strcpy(source_str, "API");
1075 break;
1076 case 0x8247:
1077 strcpy(source_str, "WINDOW_SYSTEM");
1078 break;
1079 case 0x8248:
1080 strcpy(source_str, "SHADER_COMPILER");
1081 break;
1082 case 0x8249:
1083 strcpy(source_str, "THIRD_PARTY");
1084 break;
1085 case 0x824A:
1086 strcpy(source_str, "APPLICATION");
1087 break;
1088 case 0x824B:
1089 strcpy(source_str, "OTHER");
1090 break;
1091 default:
1092 strcpy(source_str, "undefined");
1093 break;
1094 }
1095
1096 switch (type) {
1097 case 0x824C:
1098 strcpy(type_str, "ERROR");
1099 break;
1100 case 0x824D:
1101 strcpy(type_str, "DEPRECATED_BEHAVIOR");
1102 break;
1103 case 0x824E:
1104 strcpy(type_str, "UNDEFINED_BEHAVIOR");
1105 break;
1106 case 0x824F:
1107 strcpy(type_str, "PORTABILITY");
1108 break;
1109 case 0x8250:
1110 strcpy(type_str, "PERFORMANCE");
1111 break;
1112 case 0x8251:
1113 strcpy(type_str, "OTHER");
1114 break;
1115 case 0x8268:
1116 strcpy(type_str, "MARKER");
1117 break;
1118 case 0x8269:
1119 strcpy(type_str, "PUSH_GROUP");
1120 break;
1121 case 0x826A:
1122 strcpy(type_str, "POP_GROUP");
1123 break;
1124 default:
1125 strcpy(type_str, "undefined");
1126 break;
1127 }
1128 switch (severity) {
1129 case 0x9146:
1130 strcpy(severity_str, "HIGH");
1131 break;
1132 case 0x9147:
1133 strcpy(severity_str, "MEDIUM");
1134 break;
1135 case 0x9148:
1136 strcpy(severity_str, "LOW");
1137 break;
1138 case 0x826B:
1139 strcpy(severity_str, "NOTIFICATION");
1140 break;
1141 default:
1142 strcpy(severity_str, "undefined");
1143 break;
1144 }
1145
1146 if (string(severity_str) != "NOTIFICATION") {
1147 cout << "OpenGL Error!!!" << endl;
1148 cout << "Source: " << string(source_str) << endl;
1149 cout << "Type: " << string(type_str) << endl;
1150 cout << "Severity: " << string(severity_str) << endl;
1151 cout << strMessage << endl;
1152 }
1153}
1154
1155
1156GLuint loadShader(GLenum type, string file) {
1157 cout << "Loading shader from file " << file << endl;
1158
1159 ifstream shaderFile(file);
1160 GLuint shaderId = 0;
1161
1162 if (shaderFile.is_open()) {
1163 string line, shaderString;
1164
1165 while(getline(shaderFile, line)) {
1166 shaderString += line + "\n";
1167 }
1168 shaderFile.close();
1169 const char* shaderCString = shaderString.c_str();
1170
1171 shaderId = glCreateShader(type);
1172 glShaderSource(shaderId, 1, &shaderCString, NULL);
1173 glCompileShader(shaderId);
1174
1175 cout << "Loaded successfully" << endl;
1176 } else {
1177 cout << "Failed to load the file" << endl;
1178 }
1179
1180 return shaderId;
1181}
1182
1183GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath) {
1184 GLuint vs = loadShader(GL_VERTEX_SHADER, vertexShaderPath);
1185 GLuint fs = loadShader(GL_FRAGMENT_SHADER, fragmentShaderPath);
1186
1187 GLuint shader_program = glCreateProgram();
1188 glAttachShader(shader_program, vs);
1189 glAttachShader(shader_program, fs);
1190
1191 glLinkProgram(shader_program);
1192
1193 return shader_program;
1194}
1195
1196unsigned char* loadImage(string file_name, int* x, int* y) {
1197 int n;
1198 int force_channels = 4; // This forces RGBA (4 bytes per pixel)
1199 unsigned char* image_data = stbi_load(file_name.c_str(), x, y, &n, force_channels);
1200
1201 int width_in_bytes = *x * 4;
1202 unsigned char *top = NULL;
1203 unsigned char *bottom = NULL;
1204 unsigned char temp = 0;
1205 int half_height = *y / 2;
1206
1207 // flip image upside-down to account for OpenGL treating lower-left as (0, 0)
1208 for (int row = 0; row < half_height; row++) {
1209 top = image_data + row * width_in_bytes;
1210 bottom = image_data + (*y - row - 1) * width_in_bytes;
1211 for (int col = 0; col < width_in_bytes; col++) {
1212 temp = *top;
1213 *top = *bottom;
1214 *bottom = temp;
1215 top++;
1216 bottom++;
1217 }
1218 }
1219
1220 if (!image_data) {
1221 fprintf(stderr, "ERROR: could not load %s\n", file_name.c_str());
1222 }
1223
1224 // Not Power-of-2 check
1225 if ((*x & (*x - 1)) != 0 || (*y & (*y - 1)) != 0) {
1226 fprintf(stderr, "WARNING: texture %s is not power-of-2 dimensions\n", file_name.c_str());
1227 }
1228
1229 return image_data;
1230}
1231
1232bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point) {
1233 // LINE EQUATION: P = O + Dt
1234 // O = cam
1235 // D = ray_world
1236
1237 // PLANE EQUATION: P dot n + d = 0
1238 // n is the normal vector
1239 // d is the offset from the origin
1240
1241 // Take the cross-product of two vectors on the plane to get the normal
1242 vec3 v1 = points[1] - points[0];
1243 vec3 v2 = points[2] - points[0];
1244
1245 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);
1246
1247 vec3 local_ray = vec3(inverse(obj->model_mat) * world_ray);
1248 vec3 local_cam = vec3(inverse(obj->model_mat) * cam);
1249
1250 local_ray = local_ray - local_cam;
1251
1252 float d = -glm::dot(points[0], normal);
1253 float t = -(glm::dot(local_cam, normal) + d) / glm::dot(local_ray, normal);
1254
1255 vec3 intersection = local_cam + t*local_ray;
1256
1257 if (insideTriangle(intersection, points)) {
1258 click_point = obj->model_mat * vec4(intersection, 1.0f);
1259 return true;
1260 } else {
1261 return false;
1262 }
1263}
1264
1265bool insideTriangle(vec3 p, array<vec3, 3> triangle_points) {
1266 vec3 v21 = triangle_points[1] - triangle_points[0];
1267 vec3 v31 = triangle_points[2] - triangle_points[0];
1268 vec3 pv1 = p - triangle_points[0];
1269
1270 float y = (pv1.y*v21.x - pv1.x*v21.y) / (v31.y*v21.x - v31.x*v21.y);
1271 float x = (pv1.x-y*v31.x) / v21.x;
1272
1273 return x > 0.0f && y > 0.0f && x+y < 1.0f;
1274}
1275
1276void printVector(string label, vec3& v) {
1277 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << ")" << endl;
1278}
1279
1280void print4DVector(string label, vec4& v) {
1281 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << "," << v.w << ")" << endl;
1282}
1283
1284void initObject(SceneObject* obj) {
1285 // Each objects must have at least 3 points, so the size of
1286 // the points array must be a positive multiple of 9
1287 if (obj->points.size() == 0 || (obj->points.size() % 9) != 0) {
1288 // TODO: Maybe throw some kind of error here instead
1289 return;
1290 }
1291
1292 obj->id = objects.size(); // currently unused
1293 obj->num_points = obj->points.size() / 3;
1294 obj->model_transform = mat4(1.0f);
1295 obj->deleted = false;
1296
1297 obj->normals.reserve(obj->points.size());
1298 for (unsigned int i = 0; i < obj->points.size(); i += 9) {
1299 vec3 point1 = vec3(obj->points[i], obj->points[i + 1], obj->points[i + 2]);
1300 vec3 point2 = vec3(obj->points[i + 3], obj->points[i + 4], obj->points[i + 5]);
1301 vec3 point3 = vec3(obj->points[i + 6], obj->points[i + 7], obj->points[i + 8]);
1302
1303 vec3 normal = normalize(cross(point2 - point1, point3 - point1));
1304
1305 // Add the same normal for all 3 points
1306 for (int j = 0; j < 3; j++) {
1307 obj->normals.push_back(normal.x);
1308 obj->normals.push_back(normal.y);
1309 obj->normals.push_back(normal.z);
1310 }
1311 }
1312
1313 if (obj->type == TYPE_SHIP || obj->type == TYPE_ASTEROID) {
1314 calculateObjectBoundingBox(obj);
1315
1316 obj->bounding_center = vec3(obj->translate_mat * vec4(obj->bounding_center, 1.0f));
1317 }
1318}
1319
1320void addObjectToScene(SceneObject* obj,
1321 map<GLuint, BufferInfo>& shaderBufferInfo,
1322 map<ObjectType, ShaderModelGroup>& modelGroups,
1323 GLuint points_vbo,
1324 GLuint colors_vbo,
1325 GLuint texcoords_vbo,
1326 GLuint normals_vbo,
1327 GLuint ubo,
1328 GLuint model_mat_idx_vbo) {
1329 objects.push_back(obj);
1330
1331 BufferInfo* bufferInfo = &shaderBufferInfo[obj->shader_program];
1332
1333 // Check if the buffers aren't large enough to fit the new object and, if so, call
1334 // populateBuffers() to resize and repopupulate them
1335 if (bufferInfo->vbo_capacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
1336 bufferInfo->ubo_capacity < (bufferInfo->ubo_offset + 1)) {
1337
1338 if (leftLaser != NULL && leftLaser->deleted) {
1339 leftLaser = NULL;
1340 }
1341 if (rightLaser != NULL && rightLaser->deleted) {
1342 rightLaser = NULL;
1343 }
1344
1345 populateBuffers(objects, shaderBufferInfo, modelGroups,
1346 points_vbo,
1347 colors_vbo,
1348 texcoords_vbo,
1349 normals_vbo,
1350 ubo,
1351 model_mat_idx_vbo);
1352 } else {
1353 copyObjectDataToBuffers(*objects.back(), shaderBufferInfo, modelGroups,
1354 points_vbo,
1355 colors_vbo,
1356 texcoords_vbo,
1357 normals_vbo,
1358 ubo,
1359 model_mat_idx_vbo);
1360 }
1361}
1362
1363void removeObjectFromScene(SceneObject& obj, GLuint ubo) {
1364 if (!obj.deleted) {
1365 // Move the object outside the render bounds of the scene so it doesn't get rendered
1366 // TODO: Find a better way of hiding the object until the next time buffers are repopulated
1367 transformObject(obj, translate(mat4(1.0f), vec3(0.0f, 0.0f, FAR_CLIP * 1000.0f)), ubo);
1368 obj.deleted = true;
1369 }
1370}
1371
1372void calculateObjectBoundingBox(SceneObject* obj) {
1373 GLfloat min_x = obj->points[0];
1374 GLfloat max_x = obj->points[0];
1375 GLfloat min_y = obj->points[1];
1376 GLfloat max_y = obj->points[1];
1377 GLfloat min_z = obj->points[2];
1378 GLfloat max_z = obj->points[2];
1379
1380 // start from the second point
1381 for (unsigned int i = 3; i < obj->points.size(); i += 3) {
1382 if (min_x > obj->points[i]) {
1383 min_x = obj->points[i];
1384 }
1385 else if (max_x < obj->points[i]) {
1386 max_x = obj->points[i];
1387 }
1388
1389 if (min_y > obj->points[i + 1]) {
1390 min_y = obj->points[i + 1];
1391 }
1392 else if (max_y < obj->points[i + 1]) {
1393 max_y = obj->points[i + 1];
1394 }
1395
1396 if (min_z > obj->points[i + 2]) {
1397 min_z = obj->points[i + 2];
1398 }
1399 else if (max_z < obj->points[i + 2]) {
1400 max_z = obj->points[i + 2];
1401 }
1402 }
1403
1404 obj->bounding_center = vec3((min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f);
1405
1406 GLfloat radius_x = max_x - obj->bounding_center.x;
1407 GLfloat radius_y = max_y - obj->bounding_center.y;
1408 GLfloat radius_z = max_z - obj->bounding_center.z;
1409
1410 // TODO: This actually underestimates the radius. Might need to be fixed at some point.
1411 // TODO: Does not take into account any scaling in the model matrix
1412 obj->bounding_radius = radius_x;
1413 if (obj->bounding_radius < radius_y)
1414 obj->bounding_radius = radius_y;
1415 if (obj->bounding_radius < radius_z)
1416 obj->bounding_radius = radius_z;
1417
1418 for (unsigned int i = 0; i < obj->points.size(); i += 3) {
1419 obj->points[i] -= obj->bounding_center.x;
1420 obj->points[i + 1] -= obj->bounding_center.y;
1421 obj->points[i + 2] -= obj->bounding_center.z;
1422 }
1423
1424 obj->bounding_center = vec3(0.0f, 0.0f, 0.0f);
1425}
1426
1427SceneObject* createShip(GLuint shader) {
1428 SceneObject* ship = new SceneObject();
1429
1430 ship->type = TYPE_SHIP;
1431 ship->shader_program = shader;
1432
1433 ship->points = {
1434 //back
1435 -0.5f, 0.3f, 0.0f,
1436 -0.5f, 0.0f, 0.0f,
1437 0.5f, 0.0f, 0.0f,
1438 -0.5f, 0.3f, 0.0f,
1439 0.5f, 0.0f, 0.0f,
1440 0.5f, 0.3f, 0.0f,
1441
1442 // left back
1443 -0.5f, 0.3f, -2.0f,
1444 -0.5f, 0.0f, -2.0f,
1445 -0.5f, 0.0f, 0.0f,
1446 -0.5f, 0.3f, -2.0f,
1447 -0.5f, 0.0f, 0.0f,
1448 -0.5f, 0.3f, 0.0f,
1449
1450 // right back
1451 0.5f, 0.3f, 0.0f,
1452 0.5f, 0.0f, 0.0f,
1453 0.5f, 0.0f, -2.0f,
1454 0.5f, 0.3f, 0.0f,
1455 0.5f, 0.0f, -2.0f,
1456 0.5f, 0.3f, -2.0f,
1457
1458 // left mid
1459 -0.25f, 0.3f, -3.0f,
1460 -0.25f, 0.0f, -3.0f,
1461 -0.5f, 0.0f, -2.0f,
1462 -0.25f, 0.3f, -3.0f,
1463 -0.5f, 0.0f, -2.0f,
1464 -0.5f, 0.3f, -2.0f,
1465
1466 // right mid
1467 0.5f, 0.3f, -2.0f,
1468 0.5f, 0.0f, -2.0f,
1469 0.25f, 0.0f, -3.0f,
1470 0.5f, 0.3f, -2.0f,
1471 0.25f, 0.0f, -3.0f,
1472 0.25f, 0.3f, -3.0f,
1473
1474 // left front
1475 0.0f, 0.0f, -3.5f,
1476 -0.25f, 0.0f, -3.0f,
1477 -0.25f, 0.3f, -3.0f,
1478
1479 // right front
1480 0.25f, 0.3f, -3.0f,
1481 0.25f, 0.0f, -3.0f,
1482 0.0f, 0.0f, -3.5f,
1483
1484 // top back
1485 -0.5f, 0.3f, -2.0f,
1486 -0.5f, 0.3f, 0.0f,
1487 0.5f, 0.3f, 0.0f,
1488 -0.5f, 0.3f, -2.0f,
1489 0.5f, 0.3f, 0.0f,
1490 0.5f, 0.3f, -2.0f,
1491
1492 // bottom back
1493 -0.5f, 0.0f, 0.0f,
1494 -0.5f, 0.0f, -2.0f,
1495 0.5f, 0.0f, 0.0f,
1496 0.5f, 0.0f, 0.0f,
1497 -0.5f, 0.0f, -2.0f,
1498 0.5f, 0.0f, -2.0f,
1499
1500 // top mid
1501 -0.25f, 0.3f, -3.0f,
1502 -0.5f, 0.3f, -2.0f,
1503 0.5f, 0.3f, -2.0f,
1504 -0.25f, 0.3f, -3.0f,
1505 0.5f, 0.3f, -2.0f,
1506 0.25f, 0.3f, -3.0f,
1507
1508 // bottom mid
1509 -0.5f, 0.0f, -2.0f,
1510 -0.25f, 0.0f, -3.0f,
1511 0.5f, 0.0f, -2.0f,
1512 0.5f, 0.0f, -2.0f,
1513 -0.25f, 0.0f, -3.0f,
1514 0.25f, 0.0f, -3.0f,
1515
1516 // top front
1517 -0.25f, 0.3f, -3.0f,
1518 0.25f, 0.3f, -3.0f,
1519 0.0f, 0.0f, -3.5f,
1520
1521 // bottom front
1522 0.25f, 0.0f, -3.0f,
1523 -0.25f, 0.0f, -3.0f,
1524 0.0f, 0.0f, -3.5f,
1525
1526 // left wing start back
1527 -1.5f, 0.3f, 0.0f,
1528 -1.5f, 0.0f, 0.0f,
1529 -0.5f, 0.0f, 0.0f,
1530 -1.5f, 0.3f, 0.0f,
1531 -0.5f, 0.0f, 0.0f,
1532 -0.5f, 0.3f, 0.0f,
1533
1534 // left wing start top
1535 -0.5f, 0.3f, -0.3f,
1536 -1.3f, 0.3f, -0.3f,
1537 -1.5f, 0.3f, 0.0f,
1538 -0.5f, 0.3f, -0.3f,
1539 -1.5f, 0.3f, 0.0f,
1540 -0.5f, 0.3f, 0.0f,
1541
1542 // left wing start front
1543 -0.5f, 0.3f, -0.3f,
1544 -0.5f, 0.0f, -0.3f,
1545 -1.3f, 0.0f, -0.3f,
1546 -0.5f, 0.3f, -0.3f,
1547 -1.3f, 0.0f, -0.3f,
1548 -1.3f, 0.3f, -0.3f,
1549
1550 // left wing start bottom
1551 -0.5f, 0.0f, 0.0f,
1552 -1.5f, 0.0f, 0.0f,
1553 -1.3f, 0.0f, -0.3f,
1554 -0.5f, 0.0f, 0.0f,
1555 -1.3f, 0.0f, -0.3f,
1556 -0.5f, 0.0f, -0.3f,
1557
1558 // left wing end outside
1559 -1.5f, 0.3f, 0.0f,
1560 -2.2f, 0.15f, -0.8f,
1561 -1.5f, 0.0f, 0.0f,
1562
1563 // left wing end top
1564 -1.3f, 0.3f, -0.3f,
1565 -2.2f, 0.15f, -0.8f,
1566 -1.5f, 0.3f, 0.0f,
1567
1568 // left wing end front
1569 -1.3f, 0.0f, -0.3f,
1570 -2.2f, 0.15f, -0.8f,
1571 -1.3f, 0.3f, -0.3f,
1572
1573 // left wing end bottom
1574 -1.5f, 0.0f, 0.0f,
1575 -2.2f, 0.15f, -0.8f,
1576 -1.3f, 0.0f, -0.3f,
1577
1578 // right wing start back
1579 1.5f, 0.0f, 0.0f,
1580 1.5f, 0.3f, 0.0f,
1581 0.5f, 0.0f, 0.0f,
1582 0.5f, 0.0f, 0.0f,
1583 1.5f, 0.3f, 0.0f,
1584 0.5f, 0.3f, 0.0f,
1585
1586 // right wing start top
1587 1.3f, 0.3f, -0.3f,
1588 0.5f, 0.3f, -0.3f,
1589 1.5f, 0.3f, 0.0f,
1590 1.5f, 0.3f, 0.0f,
1591 0.5f, 0.3f, -0.3f,
1592 0.5f, 0.3f, 0.0f,
1593
1594 // right wing start front
1595 0.5f, 0.0f, -0.3f,
1596 0.5f, 0.3f, -0.3f,
1597 1.3f, 0.0f, -0.3f,
1598 1.3f, 0.0f, -0.3f,
1599 0.5f, 0.3f, -0.3f,
1600 1.3f, 0.3f, -0.3f,
1601
1602 // right wing start bottom
1603 1.5f, 0.0f, 0.0f,
1604 0.5f, 0.0f, 0.0f,
1605 1.3f, 0.0f, -0.3f,
1606 1.3f, 0.0f, -0.3f,
1607 0.5f, 0.0f, 0.0f,
1608 0.5f, 0.0f, -0.3f,
1609
1610 // right wing end outside
1611 2.2f, 0.15f, -0.8f,
1612 1.5f, 0.3f, 0.0f,
1613 1.5f, 0.0f, 0.0f,
1614
1615 // right wing end top
1616 2.2f, 0.15f, -0.8f,
1617 1.3f, 0.3f, -0.3f,
1618 1.5f, 0.3f, 0.0f,
1619
1620 // right wing end front
1621 2.2f, 0.15f, -0.8f,
1622 1.3f, 0.0f, -0.3f,
1623 1.3f, 0.3f, -0.3f,
1624
1625 // right wing end bottom
1626 2.2f, 0.15f, -0.8f,
1627 1.5f, 0.0f, 0.0f,
1628 1.3f, 0.0f, -0.3f,
1629 };
1630 ship->colors = {
1631 0.0f, 0.0f, 0.3f,
1632 0.0f, 0.0f, 0.3f,
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
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 0.0f, 0.0f, 0.3f,
1644
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 0.0f, 0.0f, 0.3f,
1651
1652 0.0f, 0.0f, 0.3f,
1653 0.0f, 0.0f, 0.3f,
1654 0.0f, 0.0f, 0.3f,
1655 0.0f, 0.0f, 0.3f,
1656 0.0f, 0.0f, 0.3f,
1657 0.0f, 0.0f, 0.3f,
1658
1659 0.0f, 0.0f, 0.3f,
1660 0.0f, 0.0f, 0.3f,
1661 0.0f, 0.0f, 0.3f,
1662 0.0f, 0.0f, 0.3f,
1663 0.0f, 0.0f, 0.3f,
1664 0.0f, 0.0f, 0.3f,
1665
1666 0.0f, 0.0f, 1.0f,
1667 0.0f, 0.0f, 1.0f,
1668 0.0f, 0.0f, 1.0f,
1669
1670 0.0f, 0.0f, 1.0f,
1671 0.0f, 0.0f, 1.0f,
1672 0.0f, 0.0f, 1.0f,
1673
1674 0.0f, 0.0f, 1.0f,
1675 0.0f, 0.0f, 1.0f,
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
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 0.0f, 0.0f, 1.0f,
1687
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 0.0f, 0.0f, 1.0f,
1694
1695 0.0f, 0.0f, 1.0f,
1696 0.0f, 0.0f, 1.0f,
1697 0.0f, 0.0f, 1.0f,
1698 0.0f, 0.0f, 1.0f,
1699 0.0f, 0.0f, 1.0f,
1700 0.0f, 0.0f, 1.0f,
1701
1702 0.0f, 0.0f, 0.3f,
1703 0.0f, 0.0f, 0.3f,
1704 0.0f, 0.0f, 0.3f,
1705
1706 0.0f, 0.0f, 0.3f,
1707 0.0f, 0.0f, 0.3f,
1708 0.0f, 0.0f, 0.3f,
1709
1710 0.0f, 0.0f, 0.3f,
1711 0.0f, 0.0f, 0.3f,
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
1717 0.0f, 0.0f, 0.3f,
1718 0.0f, 0.0f, 0.3f,
1719 0.0f, 0.0f, 0.3f,
1720 0.0f, 0.0f, 0.3f,
1721 0.0f, 0.0f, 0.3f,
1722 0.0f, 0.0f, 0.3f,
1723
1724 0.0f, 0.0f, 0.3f,
1725 0.0f, 0.0f, 0.3f,
1726 0.0f, 0.0f, 0.3f,
1727 0.0f, 0.0f, 0.3f,
1728 0.0f, 0.0f, 0.3f,
1729 0.0f, 0.0f, 0.3f,
1730
1731 0.0f, 0.0f, 0.3f,
1732 0.0f, 0.0f, 0.3f,
1733 0.0f, 0.0f, 0.3f,
1734 0.0f, 0.0f, 0.3f,
1735 0.0f, 0.0f, 0.3f,
1736 0.0f, 0.0f, 0.3f,
1737
1738 0.0f, 0.0f, 0.3f,
1739 0.0f, 0.0f, 0.3f,
1740 0.0f, 0.0f, 0.3f,
1741
1742 0.0f, 0.0f, 0.3f,
1743 0.0f, 0.0f, 0.3f,
1744 0.0f, 0.0f, 0.3f,
1745
1746 0.0f, 0.0f, 0.3f,
1747 0.0f, 0.0f, 0.3f,
1748 0.0f, 0.0f, 0.3f,
1749
1750 0.0f, 0.0f, 0.3f,
1751 0.0f, 0.0f, 0.3f,
1752 0.0f, 0.0f, 0.3f,
1753
1754 0.0f, 0.0f, 0.3f,
1755 0.0f, 0.0f, 0.3f,
1756 0.0f, 0.0f, 0.3f,
1757 0.0f, 0.0f, 0.3f,
1758 0.0f, 0.0f, 0.3f,
1759 0.0f, 0.0f, 0.3f,
1760
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 0.0f, 0.0f, 0.3f,
1767
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 0.0f, 0.0f, 0.3f,
1774
1775 0.0f, 0.0f, 0.3f,
1776 0.0f, 0.0f, 0.3f,
1777 0.0f, 0.0f, 0.3f,
1778 0.0f, 0.0f, 0.3f,
1779 0.0f, 0.0f, 0.3f,
1780 0.0f, 0.0f, 0.3f,
1781
1782 0.0f, 0.0f, 0.3f,
1783 0.0f, 0.0f, 0.3f,
1784 0.0f, 0.0f, 0.3f,
1785
1786 0.0f, 0.0f, 0.3f,
1787 0.0f, 0.0f, 0.3f,
1788 0.0f, 0.0f, 0.3f,
1789
1790 0.0f, 0.0f, 0.3f,
1791 0.0f, 0.0f, 0.3f,
1792 0.0f, 0.0f, 0.3f,
1793
1794 0.0f, 0.0f, 0.3f,
1795 0.0f, 0.0f, 0.3f,
1796 0.0f, 0.0f, 0.3f,
1797 };
1798 ship->texcoords = { 0.0f };
1799
1800 mat4 T_model = translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f));
1801 mat4 R_model(1.0f);
1802 ship->model_base = T_model * R_model * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
1803
1804 ship->translate_mat = T_model;
1805
1806 initObject(ship);
1807
1808 return ship;
1809}
1810
1811/* LASER RENDERING/POSITIONING ALGORITHM
1812 * -Draw a thin rectangle for the laser beam, using the specified width and endpoints
1813 * -Texture the beam with a grayscale partially transparent image
1814 * -In the shader, blend the image with a color to support lasers of different colors
1815 *
1816 * The flat part of the textured rectangle needs to always face the camera, so the laser's width is constant
1817 * This is done as follows:
1818* -Determine the length of the laser based on the start and end points
1819* -Draw a rectangle along the z-axis and rotated upwards along the y-axis, with the correct final length and width
1820* -Rotate the beam around the z-axis by the correct angle, sot that in its final position, the flat part faces the camera
1821* -Rotate the beam along the x-axis and then along the y-axis and then translate it to put it into its final position
1822*/
1823// TODO: Make the color parameter have an effect
1824Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width, GLuint laser_sp) {
1825 Laser* obj = new Laser();
1826 obj->type = TYPE_LASER;
1827 obj->targetAsteroid = NULL;
1828 obj->shader_program = laser_sp;
1829
1830 vec3 ray = end - start;
1831 float length = glm::length(ray);
1832
1833 obj->points = {
1834 width / 2, 0.0f, -width / 2,
1835 -width / 2, 0.0f, -width / 2,
1836 -width / 2, 0.0f, 0.0f,
1837 width / 2, 0.0f, -width / 2,
1838 -width / 2, 0.0f, 0.0f,
1839 width / 2, 0.0f, 0.0f,
1840 width / 2, 0.0f, -length + width / 2,
1841 -width / 2, 0.0f, -length + width / 2,
1842 -width / 2, 0.0f, -width / 2,
1843 width / 2, 0.0f, -length + width / 2,
1844 -width / 2, 0.0f, -width / 2,
1845 width / 2, 0.0f, -width / 2,
1846 width / 2, 0.0f, -length,
1847 -width / 2, 0.0f, -length,
1848 -width / 2, 0.0f, -length + width / 2,
1849 width / 2, 0.0f, -length,
1850 -width / 2, 0.0f, -length + width / 2,
1851 width / 2, 0.0f, -length + width / 2,
1852 };
1853
1854 obj->texcoords = {
1855 1.0f, 0.5f,
1856 0.0f, 0.5f,
1857 0.0f, 0.0f,
1858 1.0f, 0.5f,
1859 0.0f, 0.0f,
1860 1.0f, 0.0f,
1861 1.0f, 0.51f,
1862 0.0f, 0.51f,
1863 0.0f, 0.49f,
1864 1.0f, 0.51f,
1865 0.0f, 0.49f,
1866 1.0f, 0.49f,
1867 1.0f, 1.0f,
1868 0.0f, 1.0f,
1869 0.0f, 0.5f,
1870 1.0f, 1.0f,
1871 0.0f, 0.5f,
1872 1.0f, 0.5f,
1873 };
1874
1875 float xAxisRotation = asin(ray.y / length);
1876 float yAxisRotation = atan2(-ray.x, -ray.z);
1877
1878 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1879 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1880 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1881
1882 // To project point P onto line AB:
1883 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1884 vec3 projOnLaser = start + glm::dot(cam_pos-start, ray) / (length*length) * ray;
1885 vec3 laserToCam = cam_pos - projOnLaser;
1886
1887 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1888
1889 obj->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1890
1891 initObject(obj);
1892
1893 obj->model_transform = rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) * obj->model_transform;
1894 obj->model_transform = rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) * obj->model_transform;
1895 obj->model_transform = translate(mat4(1.0f), start) * obj->model_transform;
1896
1897 return obj;
1898}
1899
1900ShaderModelGroup createModelGroup(GLuint shaderProgram) {
1901 ShaderModelGroup smg;
1902
1903 smg.shaderProgram = shaderProgram;
1904 glGenVertexArrays(1, &smg.vao);
1905 smg.numPoints = 0;
1906
1907 return smg;
1908}
1909
1910void removeModelFromGroup(ShaderModelGroup& modelGroup, SceneObject& model) {
1911 // TODO: Implement
1912}
1913
1914void addModelToGroup(ShaderModelGroup& modelGroup, SceneObject& model) {
1915 // TODO: Implement
1916}
1917
1918void initializeBuffers(
1919 GLuint* points_vbo,
1920 GLuint* colors_vbo,
1921 GLuint* texcoords_vbo,
1922 GLuint* normals_vbo,
1923 GLuint* ubo,
1924 GLuint* model_mat_idx_vbo) {
1925 *points_vbo = 0;
1926 glGenBuffers(1, points_vbo);
1927
1928 *colors_vbo = 0;
1929 glGenBuffers(1, colors_vbo);
1930
1931 *texcoords_vbo = 0;
1932 glGenBuffers(1, texcoords_vbo);
1933
1934 *normals_vbo = 0;
1935 glGenBuffers(1, normals_vbo);
1936
1937 *ubo = 0;
1938 glGenBuffers(1, ubo);
1939
1940 *model_mat_idx_vbo = 0;
1941 glGenBuffers(1, model_mat_idx_vbo);
1942}
1943
1944void initializeParticleEffectBuffers(vec3 origin, mat4 proj, mat4 view,
1945 map<GLuint, BufferInfo>& shaderBufferInfo,
1946 map<ObjectType, ShaderModelGroup>& modelGroups,
1947 GLuint points_vbo,
1948 GLuint colors_vbo,
1949 GLuint texcoords_vbo,
1950 GLuint normals_vbo,
1951 GLuint ubo,
1952 GLuint model_mat_idx_vbo) {
1953 float vv[EXPLOSION_PARTICLE_COUNT * 3]; // initial velocities vec3
1954 float vt[EXPLOSION_PARTICLE_COUNT]; // initial times
1955 float t_accum = 0.0f; // start time
1956
1957 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
1958 vt[i] = t_accum;
1959 t_accum += 0.01f;
1960
1961 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
1962 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
1963 vv[i*3] = randx;
1964 vv[i*3 + 1] = randy;
1965 vv[i*3 + 2] = 0.0f;
1966 }
1967
1968 mat4 model_mat = translate(mat4(1.0f), origin);
1969
1970 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
1971
1972 GLuint proj_mat_loc = glGetUniformLocation(modelGroups[TYPE_EXPLOSION].shaderProgram, "proj");
1973 GLuint view_mat_loc = glGetUniformLocation(modelGroups[TYPE_EXPLOSION].shaderProgram, "view");
1974
1975 glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, value_ptr(proj));
1976 glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, value_ptr(view));
1977
1978 GLuint model_mat_loc = glGetUniformLocation(modelGroups[TYPE_EXPLOSION].shaderProgram, "model_mat");
1979
1980 glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, value_ptr(model_mat));
1981
1982 GLuint velocity_vbo;
1983 glGenBuffers(1, &velocity_vbo);
1984 glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
1985 glBufferData(GL_ARRAY_BUFFER, sizeof(vv), vv, GL_STATIC_DRAW);
1986
1987 GLuint time_vbo;
1988 glGenBuffers(1, &time_vbo);
1989 glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
1990 glBufferData(GL_ARRAY_BUFFER, sizeof(vt), vt, GL_STATIC_DRAW);
1991
1992 glBindVertexArray(modelGroups[TYPE_EXPLOSION].vao);
1993
1994 glEnableVertexAttribArray(0);
1995 glEnableVertexAttribArray(1);
1996
1997 glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
1998 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
1999
2000 glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
2001 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, NULL);
2002
2003 objExplosion = createExplosion(modelGroups[TYPE_EXPLOSION].shaderProgram);
2004 addObjectToScene(objExplosion, shaderBufferInfo, modelGroups,
2005 points_vbo,
2006 colors_vbo,
2007 texcoords_vbo,
2008 normals_vbo,
2009 ubo,
2010 model_mat_idx_vbo);
2011}
2012
2013void populateBuffers(vector<SceneObject*>& objects,
2014 map<GLuint, BufferInfo>& shaderBufferInfo,
2015 map<ObjectType, ShaderModelGroup>& modelGroups,
2016 GLuint points_vbo,
2017 GLuint colors_vbo,
2018 GLuint texcoords_vbo,
2019 GLuint normals_vbo,
2020 GLuint ubo,
2021 GLuint ubo_idx_vbo) {
2022 GLsizeiptr num_points = 0;
2023 GLsizeiptr num_objects = 0;
2024
2025 map<GLuint, unsigned int> shaderCounts;
2026 map<GLuint, unsigned int> shaderUboCounts;
2027
2028 map<GLuint, BufferInfo>::iterator shaderIt;
2029
2030 for (shaderIt = shaderBufferInfo.begin(); shaderIt != shaderBufferInfo.end(); shaderIt++) {
2031 shaderCounts[shaderIt->first] = 0;
2032 shaderUboCounts[shaderIt->first] = 0;
2033 }
2034
2035 vector<SceneObject*>::iterator it;
2036
2037 /* Find all shaders that need to be used and the number of objects and
2038 * number of points for each shader. Construct a map from shader id to count
2039 * of points being drawn using that shader (for thw model matrix ubo, we
2040 * need object counts instead). These will be used to get offsets into the
2041 * vertex buffer for each shader.
2042 */
2043 for (it = objects.begin(); it != objects.end(); ) {
2044 if ((*it)->deleted) {
2045 delete *it;
2046 it = objects.erase(it);
2047 } else {
2048 num_points += (*it)->num_points;
2049 num_objects++;
2050
2051 shaderCounts[(*it)->shader_program] += (*it)->num_points;
2052 shaderUboCounts[(*it)->shader_program]++;
2053
2054 it++;
2055 }
2056 }
2057
2058 // double the buffer sizes to leave room for new objects
2059 num_points *= 2;
2060 num_objects *= 2;
2061
2062 map<GLuint, unsigned int>::iterator shaderCountIt;
2063 unsigned int lastShaderCount = 0;
2064 unsigned int lastShaderUboCount = 0;
2065
2066 /*
2067 * The counts calculated above can be used to get the starting offset of
2068 * each shader in the vertex buffer. Create a map of base offsets to mark
2069 * where the data for the first object using a given shader begins. Also,
2070 * create a map of current offsets to mark where to copy data for the next
2071 * object being added.
2072 */
2073 for (shaderCountIt = shaderCounts.begin(); shaderCountIt != shaderCounts.end(); shaderCountIt++) {
2074 // When populating the buffers, leave as much empty space as space taken up by existing objects
2075 // to allow new objects to be added without immediately having to resize the buffers
2076 shaderBufferInfo[shaderCountIt->first].vbo_base = lastShaderCount * 2;
2077 shaderBufferInfo[shaderCountIt->first].ubo_base = lastShaderUboCount * 2;
2078
2079 shaderBufferInfo[shaderCountIt->first].ubo_offset = 0;
2080
2081 shaderBufferInfo[shaderCountIt->first].vbo_capacity = shaderCounts[shaderCountIt->first] * 2;
2082 shaderBufferInfo[shaderCountIt->first].ubo_capacity = shaderUboCounts[shaderCountIt->first] * 2;
2083
2084 lastShaderCount += shaderCounts[shaderCountIt->first];
2085 lastShaderUboCount += shaderUboCounts[shaderCountIt->first];
2086 }
2087
2088 map<ObjectType, ShaderModelGroup>::iterator modelGroupIt;
2089 for (modelGroupIt = modelGroups.begin(); modelGroupIt != modelGroups.end(); modelGroupIt++) {
2090 modelGroups[modelGroupIt->first].numPoints = 0;
2091 }
2092
2093 // Allocate all the buffers using the counts calculated above
2094
2095 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2096 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2097
2098 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
2099 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2100
2101 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
2102 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 2, NULL, GL_DYNAMIC_DRAW);
2103
2104 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
2105 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2106
2107 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2108 glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
2109
2110 glBindBuffer(GL_ARRAY_BUFFER, ubo_idx_vbo);
2111 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);
2112
2113 for (it = objects.begin(); it != objects.end(); it++) {
2114 copyObjectDataToBuffers(**it, shaderBufferInfo, modelGroups,
2115 points_vbo,
2116 colors_vbo,
2117 texcoords_vbo,
2118 normals_vbo,
2119 ubo,
2120 ubo_idx_vbo);
2121 }
2122}
2123
2124void copyObjectDataToBuffers(SceneObject& obj,
2125 map<GLuint, BufferInfo>& shaderBufferInfo,
2126 map<ObjectType, ShaderModelGroup>& modelGroups,
2127 GLuint points_vbo,
2128 GLuint colors_vbo,
2129 GLuint texcoords_vbo,
2130 GLuint normals_vbo,
2131 GLuint ubo,
2132 GLuint model_mat_idx_vbo) {
2133 BufferInfo* bufferInfo = &shaderBufferInfo[obj.shader_program];
2134
2135 obj.vertex_vbo_offset = bufferInfo->vbo_base + modelGroups[obj.type].numPoints;
2136 obj.ubo_offset = bufferInfo->ubo_base + bufferInfo->ubo_offset;
2137
2138 if (obj.ubo_offset == 0) {
2139 objFirst = &obj;
2140 }
2141
2142 if (obj.type != TYPE_EXPLOSION) {
2143 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
2144 for (unsigned int i = 0; i < obj.num_points; i++) {
2145 glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * sizeof(GLuint), sizeof(GLuint), &obj.ubo_offset);
2146 }
2147
2148 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2149 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.points.size() * sizeof(GLfloat), &obj.points[0]);
2150
2151 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
2152 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 2, obj.texcoords.size() * sizeof(GLfloat), &obj.texcoords[0]);
2153
2154 if (obj.type != TYPE_LASER) {
2155 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
2156 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.colors.size() * sizeof(GLfloat), &obj.colors[0]);
2157
2158 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
2159 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.normals.size() * sizeof(GLfloat), &obj.normals[0]);
2160 }
2161
2162 obj.model_mat = obj.model_transform * obj.model_base;
2163 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2164 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2165
2166 if (obj.type == TYPE_ASTEROID) {
2167 glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
2168
2169 ostringstream oss;
2170 oss << "hp[" << obj.ubo_offset << "]";
2171 glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid*)&obj)->hp);
2172 }
2173 }
2174
2175 modelGroups[obj.type].numPoints += obj.num_points;
2176 bufferInfo->ubo_offset++;
2177}
2178
2179void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo) {
2180 if (obj.deleted) return;
2181
2182 obj.model_transform = transform * obj.model_transform;
2183 obj.model_mat = obj.model_transform * obj.model_base;
2184
2185 obj.bounding_center = vec3(transform * vec4(obj.bounding_center, 1.0f));
2186
2187 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2188 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2189}
2190
2191void translateLaser(Laser* laser, const vec3& translation, GLuint ubo) {
2192 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2193 // and then re-used here
2194
2195 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2196 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[38], 1.0f));
2197
2198 vec3 ray = end - start;
2199 float length = glm::length(ray);
2200
2201 float xAxisRotation = asin(ray.y / length);
2202 float yAxisRotation = atan2(-ray.x, -ray.z);
2203
2204 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2205 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2206 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2207
2208 // To project point P onto line AB:
2209 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2210 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
2211 vec3 laserToCam = cam_pos - projOnLaser;
2212
2213 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2214
2215 laser->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2216
2217 transformObject(*laser, translate(mat4(1.0f), translation), ubo);
2218}
2219
2220void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp) {
2221 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2222 // and then re-used here
2223
2224 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2225 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[2] + laser->points[20], 1.0f));
2226
2227 vec3 intersection(0.0f), closestIntersection(0.0f);
2228 Asteroid* closestAsteroid = NULL;
2229
2230 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
2231 if ((*it)->type == TYPE_ASTEROID && !(*it)->deleted && getLaserAndAsteroidIntersection(start, end, **it, intersection)) {
2232 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2233 if (closestAsteroid == NULL || intersection.z > closestIntersection.z) {
2234 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2235 closestAsteroid = (Asteroid*)*it;
2236 closestIntersection = intersection;
2237 }
2238 }
2239 }
2240
2241 float width = laser->points[0] - laser->points[2];
2242
2243 if (laser->targetAsteroid != closestAsteroid) {
2244 if (laser->targetAsteroid != NULL) {
2245 if (laser == leftLaser) {
2246 leftLaserEffect->deleted = true;
2247 } else if (laser == rightLaser) {
2248 rightLaserEffect->deleted = true;
2249 }
2250 }
2251
2252 EffectOverTime* eot = NULL;
2253
2254 if (closestAsteroid != NULL) {
2255 eot = new EffectOverTime(closestAsteroid->hp, -20.0f, closestAsteroid);
2256 effects.push_back(eot);
2257 }
2258
2259 if (laser == leftLaser) {
2260 leftLaserEffect = eot;
2261 } else if (laser == rightLaser) {
2262 rightLaserEffect = eot;
2263 }
2264 }
2265 laser->targetAsteroid = closestAsteroid;
2266
2267 float length = 5.24f; // I think this was to make sure the laser went past the end of the screen
2268 if (closestAsteroid != NULL) {
2269 length = glm::length(closestIntersection - start);
2270
2271 // TODO: Find a more generic way of updating the laser hp than in updateLaserTarget
2272
2273 glUseProgram(asteroid_sp);
2274
2275 ostringstream oss;
2276 oss << "hp[" << closestAsteroid->ubo_offset << "]";
2277 glUniform1f(glGetUniformLocation(asteroid_sp, oss.str().c_str()), closestAsteroid->hp);
2278 }
2279
2280 laser->points[20] = -length + width / 2;
2281 laser->points[23] = -length + width / 2;
2282 laser->points[29] = -length + width / 2;
2283 laser->points[38] = -length;
2284 laser->points[41] = -length;
2285 laser->points[44] = -length + width / 2;
2286 laser->points[47] = -length;
2287 laser->points[50] = -length + width / 2;
2288 laser->points[53] = -length + width / 2;
2289
2290 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2291 glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * sizeof(GLfloat) * 3, laser->points.size() * sizeof(GLfloat), &laser->points[0]);
2292}
2293
2294bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection) {
2295 /*
2296 ### LINE EQUATIONS ###
2297 x = x1 + u * (x2 - x1)
2298 y = y1 + u * (y2 - y1)
2299 z = z1 + u * (z2 - z1)
2300
2301 ### SPHERE EQUATION ###
2302 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2303
2304 ### QUADRATIC EQUATION TO SOLVE ###
2305 a*u^2 + b*u + c = 0
2306 WHERE THE CONSTANTS ARE
2307 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2308 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2309 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2310
2311 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2312
2313 If the value under the root is >= 0, we got an intersection
2314 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2315 one closer to the laser start point
2316 */
2317
2318 vec3& center = asteroid.bounding_center;
2319
2320 float a = pow(end.x-start.x, 2) + pow(end.y-start.y, 2) + pow(end.z-start.z, 2);
2321 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));
2322 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);
2323 float discriminant = pow(b, 2) - 4*a*c;
2324
2325 if (discriminant >= 0.0f) {
2326 // In this case, the negative root will always give the point closer to the laser start point
2327 float u = (-b - sqrt(discriminant)) / (2 * a);
2328
2329 // Check that the intersection is within the line segment corresponding to the laser
2330 if (0.0f <= u && u <= 1.0f) {
2331 intersection = start + u * (end - start);
2332 return true;
2333 }
2334 }
2335
2336 return false;
2337}
2338
2339void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
2340 map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo) {
2341
2342 glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
2343 glBindVertexArray(modelGroups[TYPE_SHIP].vao);
2344
2345 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[modelGroups[TYPE_SHIP].shaderProgram].vbo_base, modelGroups[TYPE_SHIP].numPoints);
2346
2347 glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
2348 glBindVertexArray(modelGroups[TYPE_ASTEROID].vao);
2349
2350 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[modelGroups[TYPE_ASTEROID].shaderProgram].vbo_base, modelGroups[TYPE_ASTEROID].numPoints);
2351
2352 glEnable(GL_BLEND);
2353
2354 glUseProgram(modelGroups[TYPE_LASER].shaderProgram);
2355 glBindVertexArray(modelGroups[TYPE_LASER].vao);
2356
2357 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[modelGroups[TYPE_LASER].shaderProgram].vbo_base, modelGroups[TYPE_LASER].numPoints);
2358
2359 glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
2360 glBindVertexArray(modelGroups[TYPE_EXPLOSION].vao);
2361
2362 glEnable(GL_PROGRAM_POINT_SIZE);
2363
2364 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2365 glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(mat4), value_ptr(objExplosion->model_mat));
2366
2367 glDrawArrays(GL_POINTS, 0, modelGroups[TYPE_EXPLOSION].numPoints);
2368
2369 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2370 glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(mat4), value_ptr(objFirst->model_mat));
2371
2372 glDisable(GL_PROGRAM_POINT_SIZE);
2373 glDisable(GL_BLEND);
2374}
2375
2376void renderSceneGui() {
2377 ImGui_ImplGlfwGL3_NewFrame();
2378
2379 // 1. Show a simple window.
2380 // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
2381 /*
2382 {
2383 static float f = 0.0f;
2384 static int counter = 0;
2385 ImGui::Text("Hello, world!"); // Display some text (you can use a format string too)
2386 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
2387 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
2388
2389 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state
2390 ImGui::Checkbox("Another Window", &show_another_window);
2391
2392 if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated)
2393 counter++;
2394 ImGui::SameLine();
2395 ImGui::Text("counter = %d", counter);
2396
2397 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
2398 }
2399 */
2400
2401 stringstream ssScore, ssFps;
2402 ssScore << "Score: " << score;
2403 ssFps << "FPS: " << fps;
2404
2405 {
2406 ImGui::SetNextWindowSize(ImVec2(95, 46), ImGuiCond_Once);
2407 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
2408 ImGui::Begin("WndStats", NULL,
2409 ImGuiWindowFlags_NoTitleBar |
2410 ImGuiWindowFlags_NoResize |
2411 ImGuiWindowFlags_NoMove);
2412 ImGui::Text(ssScore.str().c_str());
2413 ImGui::Text(ssFps.str().c_str());
2414 ImGui::End();
2415 }
2416
2417 {
2418 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
2419 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
2420 ImGui::Begin("WndMenubar", NULL,
2421 ImGuiWindowFlags_NoTitleBar |
2422 ImGuiWindowFlags_NoResize |
2423 ImGuiWindowFlags_NoMove);
2424 ImGui::InvisibleButton("", ImVec2(155, 18));
2425 ImGui::SameLine();
2426 if (ImGui::Button("Main Menu")) {
2427 events.push(EVENT_GO_TO_MAIN_MENU);
2428 }
2429 ImGui::End();
2430 }
2431
2432 ImGui::Render();
2433 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2434}
2435
2436void renderMainMenu() {
2437}
2438
2439void renderMainMenuGui() {
2440 ImGui_ImplGlfwGL3_NewFrame();
2441
2442 {
2443 int padding = 4;
2444 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
2445 ImGui::SetNextWindowSize(ImVec2(width + 2 * padding, height + 2 * padding), ImGuiCond_Once);
2446 ImGui::Begin("WndMain", NULL,
2447 ImGuiWindowFlags_NoTitleBar |
2448 ImGuiWindowFlags_NoResize |
2449 ImGuiWindowFlags_NoMove);
2450
2451 ImGui::InvisibleButton("", ImVec2(10, 80));
2452 ImGui::InvisibleButton("", ImVec2(285, 18));
2453 ImGui::SameLine();
2454 if (ImGui::Button("New Game")) {
2455 events.push(EVENT_GO_TO_GAME);
2456 }
2457
2458 ImGui::InvisibleButton("", ImVec2(10, 15));
2459 ImGui::InvisibleButton("", ImVec2(300, 18));
2460 ImGui::SameLine();
2461 if (ImGui::Button("Quit")) {
2462 events.push(EVENT_QUIT);
2463 }
2464
2465 ImGui::End();
2466 }
2467
2468 ImGui::Render();
2469 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2470}
2471
2472Asteroid* createAsteroid(vec3 pos, GLuint shader) {
2473 Asteroid* obj = new Asteroid();
2474 obj->type = TYPE_ASTEROID;
2475 obj->shader_program = shader;
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(GLuint shader) {
2596 SceneObject* obj = new SceneObject();
2597 obj->type = TYPE_EXPLOSION;
2598 obj->shader_program = shader;
2599
2600 obj->points = {};
2601 obj->colors = {};
2602
2603 initObject(obj);
2604 obj->num_points = EXPLOSION_PARTICLE_COUNT;
2605
2606 return obj;
2607}
2608
2609float getRandomNum(float low, float high) {
2610 return low + ((float)rand()/RAND_MAX) * (high-low);
2611}
Note: See TracBrowser for help on using the repository browser.