source: opengl-game/new-game.cpp@ 8fbd34f

feature/imgui-sdl points-test
Last change on this file since 8fbd34f was 8fbd34f, checked in by dportnoy15 <dmitry.portnoy@…>, 6 years ago

Create a work-around for the OSX Sierra vsync issue

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