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

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

Make the explosion shader only emit particles for a short time instead of forever and make it look more like an explosion

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