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

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

Merge branch 'WIP' of medievaltech.com:opengl-game into WIP

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