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

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

Create a particle system that will later be used to render exploding asteroids

  • Property mode set to 100644
File size: 79.3 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 elapsed_system_time_loc = glGetUniformLocation(explosion_sp, "elapsed_system_time");
621
622
623 glUseProgram(color_sp);
624 glUniformMatrix4fv(view_test_loc, 1, GL_FALSE, value_ptr(view_mat));
625 glUniformMatrix4fv(proj_test_loc, 1, GL_FALSE, value_ptr(proj_mat));
626
627 glUniformBlockBinding(color_sp, color_sp_models_ub_index, ub_binding_point);
628 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
629
630
631 glUseProgram(asteroid_sp);
632 glUniformMatrix4fv(asteroid_view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
633 glUniformMatrix4fv(asteroid_proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
634
635 glUniformBlockBinding(asteroid_sp, asteroid_sp_models_ub_index, ub_binding_point);
636 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
637
638
639 glUseProgram(texture_sp);
640 glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
641 glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
642
643 glUniformBlockBinding(texture_sp, texture_sp_models_ub_index, ub_binding_point);
644 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
645
646
647 glUseProgram(laser_sp);
648 glUniformMatrix4fv(laser_view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
649 glUniformMatrix4fv(laser_proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
650 glUniform3f(laser_color_loc, 0.2f, 1.0f, 0.2f);
651
652 glUniformBlockBinding(laser_sp, laser_sp_models_ub_index, ub_binding_point);
653 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
654
655
656 bool cam_moved = false;
657
658 int frame_count = 0;
659 double elapsed_seconds_fps = 0.0f;
660 double elapsed_seconds_spawn = 0.0f;
661 double previous_seconds = glfwGetTime();
662
663 // This draws wireframes. Useful for seeing separate faces and occluded objects.
664 //glPolygonMode(GL_FRONT, GL_LINE);
665
666 // disable vsync to see real framerate
667 //glfwSwapInterval(0);
668
669 State curState = STATE_MAIN_MENU;
670
671 while (!glfwWindowShouldClose(window) && isRunning) {
672 double current_seconds = glfwGetTime();
673 double elapsed_seconds = current_seconds - previous_seconds;
674 previous_seconds = current_seconds;
675
676 elapsed_seconds_fps += elapsed_seconds;
677 if (elapsed_seconds_fps > 0.25f) {
678 fps = (double)frame_count / elapsed_seconds_fps;
679
680 frame_count = 0;
681 elapsed_seconds_fps = 0.0f;
682 }
683
684 frame_count++;
685
686 // Handle events
687
688 clickedObject = NULL;
689
690 // reset the all key states to KEY_STATE_UNCHANGED (something the GLFW key callback can never return)
691 // so that GLFW_PRESS and GLFW_RELEASE are only detected once
692 // TODO: Change this if we ever need to act on GLFW_REPEAT (which is when a key is held down
693 // continuously for a period of time)
694 fill(key_state, key_state + NUM_KEYS, KEY_STATE_UNCHANGED);
695
696 glfwPollEvents();
697
698 while (!events.empty()) {
699 switch (events.front()) {
700 case EVENT_GO_TO_MAIN_MENU:
701 curState = STATE_MAIN_MENU;
702 break;
703 case EVENT_GO_TO_GAME:
704 curState = STATE_GAME;
705 break;
706 case EVENT_QUIT:
707 isRunning = false;
708 break;
709 }
710 events.pop();
711 }
712
713 if (curState == STATE_GAME) {
714
715 elapsed_seconds_spawn += elapsed_seconds;
716 if (elapsed_seconds_spawn > 0.5f) {
717 SceneObject* obj = createAsteroid(vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f)), asteroid_sp);
718 addObjectToScene(obj, shaderBufferInfo,
719 points_vbo,
720 colors_vbo,
721 selected_colors_vbo,
722 texcoords_vbo,
723 normals_vbo,
724 ubo,
725 model_mat_idx_vbo,
726 asteroid_sp);
727
728 elapsed_seconds_spawn -= 0.5f;
729 }
730
731 /*
732 if (clickedObject == &objects[0]) {
733 selectedObject = &objects[0];
734 }
735 if (clickedObject == &objects[1]) {
736 selectedObject = &objects[1];
737 }
738 */
739
740 /*
741 if (key_state[GLFW_KEY_SPACE] == GLFW_PRESS) {
742 transformObject(objects[1], translate(mat4(1.0f), vec3(0.3f, 0.0f, 0.0f)), ubo);
743 }
744 if (key_down[GLFW_KEY_RIGHT]) {
745 transformObject(objects[2], translate(mat4(1.0f), vec3(0.01f, 0.0f, 0.0f)), ubo);
746 }
747 if (key_down[GLFW_KEY_LEFT]) {
748 transformObject(objects[2], translate(mat4(1.0f), vec3(-0.01f, 0.0f, 0.0f)), ubo);
749 }
750 */
751
752 if (key_down[GLFW_KEY_RIGHT]) {
753 transformObject(*objects[0], translate(mat4(1.0f), vec3(0.01f, 0.0f, 0.0f)), ubo);
754
755 if (leftLaser != NULL && !leftLaser->deleted) {
756 translateLaser(leftLaser, vec3(0.01f, 0.0f, 0.0f), ubo);
757 }
758 if (rightLaser != NULL && !rightLaser->deleted) {
759 translateLaser(rightLaser, vec3(0.01f, 0.0f, 0.0f), ubo);
760 }
761 }
762 if (key_down[GLFW_KEY_LEFT]) {
763 transformObject(*objects[0], translate(mat4(1.0f), vec3(-0.01f, 0.0f, 0.0f)), ubo);
764
765 if (leftLaser != NULL && !leftLaser->deleted) {
766 translateLaser(leftLaser, vec3(-0.01f, 0.0f, 0.0f), ubo);
767 }
768 if (rightLaser != NULL && !rightLaser->deleted) {
769 translateLaser(rightLaser, vec3(-0.01f, 0.0f, 0.0f), ubo);
770 }
771 }
772
773 if (key_state[GLFW_KEY_Z] == GLFW_PRESS) {
774 vec3 offset(objects[0]->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
775
776 leftLaser = createLaser(vec3(-0.21f, -1.19f, 1.76f)+offset, vec3(-0.21f, -1.19f, -3.0f)+offset,
777 vec3(0.0f, 1.0f, 0.0f), 0.03f, laser_sp);
778 addObjectToScene(leftLaser, shaderBufferInfo,
779 points_vbo,
780 colors_vbo,
781 selected_colors_vbo,
782 texcoords_vbo,
783 normals_vbo,
784 ubo,
785 model_mat_idx_vbo,
786 asteroid_sp);
787 } else if (key_state[GLFW_KEY_Z] == GLFW_RELEASE) {
788 removeObjectFromScene(*leftLaser, ubo);
789 }
790
791 if (key_state[GLFW_KEY_X] == GLFW_PRESS) {
792 vec3 offset(objects[0]->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
793
794 rightLaser = createLaser(vec3(0.21f, -1.19f, 1.76f) + offset, vec3(0.21f, -1.19f, -3.0f) + offset,
795 vec3(0.0f, 1.0f, 0.0f), 0.03f, laser_sp);
796 addObjectToScene(rightLaser, shaderBufferInfo,
797 points_vbo,
798 colors_vbo,
799 selected_colors_vbo,
800 texcoords_vbo,
801 normals_vbo,
802 ubo,
803 model_mat_idx_vbo,
804 asteroid_sp);
805 } else if (key_state[GLFW_KEY_X] == GLFW_RELEASE) {
806 removeObjectFromScene(*rightLaser, ubo);
807 }
808
809 // this code moves the asteroids
810 for (unsigned int i = 0; i < objects.size(); i++) {
811 if (objects[i]->type == TYPE_ASTEROID && !objects[i]->deleted) {
812 transformObject(*objects[i], translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.04f)), ubo);
813
814 vec3 obj_center = vec3(view_mat * vec4(objects[i]->bounding_center, 1.0f));
815
816 if ((obj_center.z - objects[i]->bounding_radius) > -NEAR_CLIP) {
817 removeObjectFromScene(*objects[i], ubo);
818 }
819 if (((Asteroid*)objects[i])->hp <= 0) {
820 removeObjectFromScene(*objects[i], ubo);
821 score++;
822 }
823 }
824 }
825
826 if (leftLaser != NULL && !leftLaser->deleted) {
827 updateLaserTarget(leftLaser, objects, points_vbo, asteroid_sp);
828 }
829 if (rightLaser != NULL && !rightLaser->deleted) {
830 updateLaserTarget(rightLaser, objects, points_vbo, asteroid_sp);
831 }
832 }
833
834 for (vector<EffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
835 if ((*it)->deleted || (*it)->effectedObject->deleted) {
836 delete *it;
837 it = effects.erase(it);
838 } else {
839 EffectOverTime* eot = *it;
840 eot->effectedValue = eot->startValue + (current_seconds - eot->startTime) * eot->changePerSecond;
841
842 it++;
843 }
844 }
845
846 if (key_state[GLFW_KEY_ESCAPE] == GLFW_PRESS) {
847 glfwSetWindowShouldClose(window, 1);
848 }
849
850 float dist = cam_speed * elapsed_seconds;
851 if (key_down[GLFW_KEY_A]) {
852 vec3 dir = vec3(inverse(R) * vec4(-1.0f, 0.0f, 0.0f, 1.0f));
853 cam_pos += dir * dist;
854
855 cam_moved = true;
856 }
857 if (key_down[GLFW_KEY_D]) {
858 vec3 dir = vec3(inverse(R) * vec4(1.0f, 0.0f, 0.0f, 1.0f));
859 cam_pos += dir * dist;
860
861 cam_moved = true;
862 }
863 if (key_down[GLFW_KEY_W]) {
864 vec3 dir = vec3(inverse(R) * vec4(0.0f, 0.0f, -1.0f, 1.0f));
865 cam_pos += dir * dist;
866
867 cam_moved = true;
868 }
869 if (key_down[GLFW_KEY_S]) {
870 vec3 dir = vec3(inverse(R) * vec4(0.0f, 0.0f, 1.0f, 1.0f));
871 cam_pos += dir * dist;
872
873 cam_moved = true;
874 }
875 /*
876 if (key_down[GLFW_KEY_LEFT]) {
877 cam_yaw += cam_yaw_speed * elapsed_seconds;
878 cam_moved = true;
879 }
880 if (key_down[GLFW_KEY_RIGHT]) {
881 cam_yaw -= cam_yaw_speed * elapsed_seconds;
882 cam_moved = true;
883 }
884 if (key_down[GLFW_KEY_UP]) {
885 cam_pitch += cam_pitch_speed * elapsed_seconds;
886 cam_moved = true;
887 }
888 if (key_down[GLFW_KEY_DOWN]) {
889 cam_pitch -= cam_pitch_speed * elapsed_seconds;
890 cam_moved = true;
891 }
892 */
893 if (cam_moved && false) { // disable camera movement
894 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
895
896 mat4 yaw_mat = rotate(mat4(1.0f), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
897 mat4 pitch_mat = rotate(mat4(1.0f), -cam_pitch, vec3(1.0f, 0.0f, 0.0f));
898 R = pitch_mat * yaw_mat;
899
900 view_mat = R * T;
901
902 //printVector("cam pos", cam_pos);
903
904 glUseProgram(color_sp);
905 glUniformMatrix4fv(view_test_loc, 1, GL_FALSE, value_ptr(view_mat));
906
907 glUseProgram(texture_sp);
908 glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
909
910 glUseProgram(laser_sp);
911 glUniformMatrix4fv(laser_view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
912
913 cam_moved = false;
914 }
915
916 glUseProgram(explosion_sp);
917 glUniform1f(elapsed_system_time_loc, (GLfloat)current_seconds);
918
919 // Render scene
920
921 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
922
923 switch (curState) {
924 case STATE_MAIN_MENU:
925 renderMainMenu();
926 renderMainMenuGui();
927 break;
928 case STATE_GAME:
929 renderScene(shaderBufferInfo,
930 color_sp, asteroid_sp, texture_sp, laser_sp, explosion_sp,
931 color_vao, asteroid_vao, texture_vao, laser_vao, explosion_vao,
932 colors_vbo, selected_colors_vbo,
933 selectedObject);
934 renderSceneGui();
935 break;
936 }
937
938 glfwSwapBuffers(window);
939 }
940
941 ImGui_ImplGlfwGL3_Shutdown();
942 ImGui::DestroyContext();
943
944 glfwDestroyWindow(window);
945 glfwTerminate();
946
947 // free memory
948
949 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
950 delete *it;
951 }
952
953 return 0;
954}
955
956void glfw_error_callback(int error, const char* description) {
957 gl_log_err("GLFW ERROR: code %i msg: %s\n", error, description);
958}
959
960void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
961 double mouse_x, mouse_y;
962 glfwGetCursorPos(window, &mouse_x, &mouse_y);
963
964 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
965 cout << "Mouse clicked (" << mouse_x << "," << mouse_y << ")" << endl;
966 selectedObject = NULL;
967
968 float x = (2.0f*mouse_x) / width - 1.0f;
969 float y = 1.0f - (2.0f*mouse_y) / height;
970
971 cout << "x: " << x << ", y: " << y << endl;
972
973 vec4 ray_clip = vec4(x, y, -1.0f, 1.0f);
974 vec4 ray_eye = inverse(proj_mat) * ray_clip;
975 ray_eye = vec4(vec2(ray_eye), -1.0f, 1.0f);
976 vec4 ray_world = inverse(view_mat) * ray_eye;
977
978 vec4 click_point;
979 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
980 SceneObject* closest_object = NULL;
981
982 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
983 if ((*it)->type == TYPE_LASER) continue;
984 for (unsigned int p_idx = 0; p_idx < (*it)->points.size(); p_idx += 9) {
985 if (faceClicked(
986 {
987 vec3((*it)->points[p_idx], (*it)->points[p_idx + 1], (*it)->points[p_idx + 2]),
988 vec3((*it)->points[p_idx + 3], (*it)->points[p_idx + 4], (*it)->points[p_idx + 5]),
989 vec3((*it)->points[p_idx + 6], (*it)->points[p_idx + 7], (*it)->points[p_idx + 8]),
990 },
991 *it, ray_world, vec4(cam_pos, 1.0f), click_point
992 )) {
993 click_point = view_mat * click_point;
994
995 if (-NEAR_CLIP >= click_point.z && click_point.z > -FAR_CLIP && click_point.z > closest_point.z) {
996 closest_point = vec3(click_point);
997 closest_object = *it;
998 }
999 }
1000 }
1001 }
1002
1003 if (closest_object == NULL) {
1004 cout << "No object was clicked" << endl;
1005 } else {
1006 clickedObject = closest_object;
1007 cout << "Clicked object: " << clickedObject->id << endl;
1008 }
1009 }
1010}
1011
1012void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
1013 key_state[key] = action;
1014
1015 // should be true for GLFW_PRESS and GLFW_REPEAT
1016 key_down[key] = (action != GLFW_RELEASE);
1017}
1018
1019void APIENTRY debugGlCallback(
1020 GLenum source,
1021 GLenum type,
1022 GLuint id,
1023 GLenum severity,
1024 GLsizei length,
1025 const GLchar* message,
1026 const void* userParam
1027) {
1028 string strMessage(message);
1029
1030 // TODO: Use C++ strings directly
1031 char source_str[2048];
1032 char type_str[2048];
1033 char severity_str[2048];
1034
1035 switch (source) {
1036 case 0x8246:
1037 strcpy(source_str, "API");
1038 break;
1039 case 0x8247:
1040 strcpy(source_str, "WINDOW_SYSTEM");
1041 break;
1042 case 0x8248:
1043 strcpy(source_str, "SHADER_COMPILER");
1044 break;
1045 case 0x8249:
1046 strcpy(source_str, "THIRD_PARTY");
1047 break;
1048 case 0x824A:
1049 strcpy(source_str, "APPLICATION");
1050 break;
1051 case 0x824B:
1052 strcpy(source_str, "OTHER");
1053 break;
1054 default:
1055 strcpy(source_str, "undefined");
1056 break;
1057 }
1058
1059 switch (type) {
1060 case 0x824C:
1061 strcpy(type_str, "ERROR");
1062 break;
1063 case 0x824D:
1064 strcpy(type_str, "DEPRECATED_BEHAVIOR");
1065 break;
1066 case 0x824E:
1067 strcpy(type_str, "UNDEFINED_BEHAVIOR");
1068 break;
1069 case 0x824F:
1070 strcpy(type_str, "PORTABILITY");
1071 break;
1072 case 0x8250:
1073 strcpy(type_str, "PERFORMANCE");
1074 break;
1075 case 0x8251:
1076 strcpy(type_str, "OTHER");
1077 break;
1078 case 0x8268:
1079 strcpy(type_str, "MARKER");
1080 break;
1081 case 0x8269:
1082 strcpy(type_str, "PUSH_GROUP");
1083 break;
1084 case 0x826A:
1085 strcpy(type_str, "POP_GROUP");
1086 break;
1087 default:
1088 strcpy(type_str, "undefined");
1089 break;
1090 }
1091 switch (severity) {
1092 case 0x9146:
1093 strcpy(severity_str, "HIGH");
1094 break;
1095 case 0x9147:
1096 strcpy(severity_str, "MEDIUM");
1097 break;
1098 case 0x9148:
1099 strcpy(severity_str, "LOW");
1100 break;
1101 case 0x826B:
1102 strcpy(severity_str, "NOTIFICATION");
1103 break;
1104 default:
1105 strcpy(severity_str, "undefined");
1106 break;
1107 }
1108
1109 if (string(severity_str) != "NOTIFICATION") {
1110 cout << "OpenGL Error!!!" << endl;
1111 cout << "Source: " << string(source_str) << endl;
1112 cout << "Type: " << string(type_str) << endl;
1113 cout << "Severity: " << string(severity_str) << endl;
1114 cout << strMessage << endl;
1115 }
1116}
1117
1118
1119GLuint loadShader(GLenum type, string file) {
1120 cout << "Loading shader from file " << file << endl;
1121
1122 ifstream shaderFile(file);
1123 GLuint shaderId = 0;
1124
1125 if (shaderFile.is_open()) {
1126 string line, shaderString;
1127
1128 while(getline(shaderFile, line)) {
1129 shaderString += line + "\n";
1130 }
1131 shaderFile.close();
1132 const char* shaderCString = shaderString.c_str();
1133
1134 shaderId = glCreateShader(type);
1135 glShaderSource(shaderId, 1, &shaderCString, NULL);
1136 glCompileShader(shaderId);
1137
1138 cout << "Loaded successfully" << endl;
1139 } else {
1140 cout << "Failed to load the file" << endl;
1141 }
1142
1143 return shaderId;
1144}
1145
1146GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath) {
1147 GLuint vs = loadShader(GL_VERTEX_SHADER, vertexShaderPath);
1148 GLuint fs = loadShader(GL_FRAGMENT_SHADER, fragmentShaderPath);
1149
1150 GLuint shader_program = glCreateProgram();
1151 glAttachShader(shader_program, vs);
1152 glAttachShader(shader_program, fs);
1153
1154 glLinkProgram(shader_program);
1155
1156 return shader_program;
1157}
1158
1159unsigned char* loadImage(string file_name, int* x, int* y) {
1160 int n;
1161 int force_channels = 4; // This forces RGBA (4 bytes per pixel)
1162 unsigned char* image_data = stbi_load(file_name.c_str(), x, y, &n, force_channels);
1163
1164 int width_in_bytes = *x * 4;
1165 unsigned char *top = NULL;
1166 unsigned char *bottom = NULL;
1167 unsigned char temp = 0;
1168 int half_height = *y / 2;
1169
1170 // flip image upside-down to account for OpenGL treating lower-left as (0, 0)
1171 for (int row = 0; row < half_height; row++) {
1172 top = image_data + row * width_in_bytes;
1173 bottom = image_data + (*y - row - 1) * width_in_bytes;
1174 for (int col = 0; col < width_in_bytes; col++) {
1175 temp = *top;
1176 *top = *bottom;
1177 *bottom = temp;
1178 top++;
1179 bottom++;
1180 }
1181 }
1182
1183 if (!image_data) {
1184 fprintf(stderr, "ERROR: could not load %s\n", file_name.c_str());
1185 }
1186
1187 // Not Power-of-2 check
1188 if ((*x & (*x - 1)) != 0 || (*y & (*y - 1)) != 0) {
1189 fprintf(stderr, "WARNING: texture %s is not power-of-2 dimensions\n", file_name.c_str());
1190 }
1191
1192 return image_data;
1193}
1194
1195bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point) {
1196 // LINE EQUATION: P = O + Dt
1197 // O = cam
1198 // D = ray_world
1199
1200 // PLANE EQUATION: P dot n + d = 0
1201 // n is the normal vector
1202 // d is the offset from the origin
1203
1204 // Take the cross-product of two vectors on the plane to get the normal
1205 vec3 v1 = points[1] - points[0];
1206 vec3 v2 = points[2] - points[0];
1207
1208 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);
1209
1210 vec3 local_ray = vec3(inverse(obj->model_mat) * world_ray);
1211 vec3 local_cam = vec3(inverse(obj->model_mat) * cam);
1212
1213 local_ray = local_ray - local_cam;
1214
1215 float d = -glm::dot(points[0], normal);
1216 float t = -(glm::dot(local_cam, normal) + d) / glm::dot(local_ray, normal);
1217
1218 vec3 intersection = local_cam + t*local_ray;
1219
1220 if (insideTriangle(intersection, points)) {
1221 click_point = obj->model_mat * vec4(intersection, 1.0f);
1222 return true;
1223 } else {
1224 return false;
1225 }
1226}
1227
1228bool insideTriangle(vec3 p, array<vec3, 3> triangle_points) {
1229 vec3 v21 = triangle_points[1] - triangle_points[0];
1230 vec3 v31 = triangle_points[2] - triangle_points[0];
1231 vec3 pv1 = p - triangle_points[0];
1232
1233 float y = (pv1.y*v21.x - pv1.x*v21.y) / (v31.y*v21.x - v31.x*v21.y);
1234 float x = (pv1.x-y*v31.x) / v21.x;
1235
1236 return x > 0.0f && y > 0.0f && x+y < 1.0f;
1237}
1238
1239void printVector(string label, vec3& v) {
1240 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << ")" << endl;
1241}
1242
1243void print4DVector(string label, vec4& v) {
1244 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << "," << v.w << ")" << endl;
1245}
1246
1247void initObject(SceneObject* obj) {
1248 // Each objects must have at least 3 points, so the size of
1249 // the points array must be a positive multiple of 9
1250 if (obj->points.size() == 0 || (obj->points.size() % 9) != 0) {
1251 // TODO: Maybe throw some kind of error here instead
1252 return;
1253 }
1254
1255 obj->id = objects.size(); // currently unused
1256 obj->num_points = obj->points.size() / 3;
1257 obj->model_transform = mat4(1.0f);
1258 obj->deleted = false;
1259
1260 obj->normals.reserve(obj->points.size());
1261 for (unsigned int i = 0; i < obj->points.size(); i += 9) {
1262 vec3 point1 = vec3(obj->points[i], obj->points[i + 1], obj->points[i + 2]);
1263 vec3 point2 = vec3(obj->points[i + 3], obj->points[i + 4], obj->points[i + 5]);
1264 vec3 point3 = vec3(obj->points[i + 6], obj->points[i + 7], obj->points[i + 8]);
1265
1266 vec3 normal = normalize(cross(point2 - point1, point3 - point1));
1267
1268 // Add the same normal for all 3 points
1269 for (int j = 0; j < 3; j++) {
1270 obj->normals.push_back(normal.x);
1271 obj->normals.push_back(normal.y);
1272 obj->normals.push_back(normal.z);
1273 }
1274 }
1275
1276 if (obj->type != TYPE_LASER) {
1277 calculateObjectBoundingBox(obj);
1278
1279 obj->bounding_center = vec3(obj->translate_mat * vec4(obj->bounding_center, 1.0f));
1280 }
1281}
1282
1283void addObjectToScene(SceneObject* obj,
1284 map<GLuint, BufferInfo>& shaderBufferInfo,
1285 GLuint points_vbo,
1286 GLuint colors_vbo,
1287 GLuint selected_colors_vbo,
1288 GLuint texcoords_vbo,
1289 GLuint normals_vbo,
1290 GLuint ubo,
1291 GLuint model_mat_idx_vbo,
1292 GLuint asteroid_sp) {
1293 objects.push_back(obj);
1294
1295 BufferInfo* bufferInfo = &shaderBufferInfo[obj->shader_program];
1296
1297 // Check if the buffers aren't large enough to fit the new object and, if so, call
1298 // populateBuffers() to resize and repopupulate them
1299 if (bufferInfo->vbo_capacity < (bufferInfo->ubo_offset + obj->num_points) ||
1300 bufferInfo->ubo_capacity < (bufferInfo->ubo_offset + 1)) {
1301
1302 if (leftLaser != NULL && leftLaser->deleted) {
1303 leftLaser = NULL;
1304 }
1305 if (rightLaser != NULL && rightLaser->deleted) {
1306 rightLaser = NULL;
1307 }
1308
1309 populateBuffers(objects, shaderBufferInfo,
1310 points_vbo,
1311 colors_vbo,
1312 selected_colors_vbo,
1313 texcoords_vbo,
1314 normals_vbo,
1315 ubo,
1316 model_mat_idx_vbo,
1317 asteroid_sp);
1318 } else {
1319 copyObjectDataToBuffers(*objects.back(), shaderBufferInfo,
1320 points_vbo,
1321 colors_vbo,
1322 selected_colors_vbo,
1323 texcoords_vbo,
1324 normals_vbo,
1325 ubo,
1326 model_mat_idx_vbo,
1327 asteroid_sp);
1328 }
1329}
1330
1331void removeObjectFromScene(SceneObject& obj, GLuint ubo) {
1332 if (!obj.deleted) {
1333 // Move the object outside the render bounds of the scene so it doesn't get rendered
1334 // TODO: Find a better way of hiding the object until the next time buffers are repopulated
1335 transformObject(obj, translate(mat4(1.0f), vec3(0.0f, 0.0f, FAR_CLIP * 1000.0f)), ubo);
1336 obj.deleted = true;
1337 }
1338}
1339
1340void calculateObjectBoundingBox(SceneObject* obj) {
1341 GLfloat min_x = obj->points[0];
1342 GLfloat max_x = obj->points[0];
1343 GLfloat min_y = obj->points[1];
1344 GLfloat max_y = obj->points[1];
1345 GLfloat min_z = obj->points[2];
1346 GLfloat max_z = obj->points[2];
1347
1348 // start from the second point
1349 for (unsigned int i = 3; i < obj->points.size(); i += 3) {
1350 if (min_x > obj->points[i]) {
1351 min_x = obj->points[i];
1352 }
1353 else if (max_x < obj->points[i]) {
1354 max_x = obj->points[i];
1355 }
1356
1357 if (min_y > obj->points[i + 1]) {
1358 min_y = obj->points[i + 1];
1359 }
1360 else if (max_y < obj->points[i + 1]) {
1361 max_y = obj->points[i + 1];
1362 }
1363
1364 if (min_z > obj->points[i + 2]) {
1365 min_z = obj->points[i + 2];
1366 }
1367 else if (max_z < obj->points[i + 2]) {
1368 max_z = obj->points[i + 2];
1369 }
1370 }
1371
1372 obj->bounding_center = vec3((min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f);
1373
1374 GLfloat radius_x = max_x - obj->bounding_center.x;
1375 GLfloat radius_y = max_y - obj->bounding_center.y;
1376 GLfloat radius_z = max_z - obj->bounding_center.z;
1377
1378 // TODO: This actually underestimates the radius. Might need to be fixed at some point.
1379 // TODO: Does not take into account any scaling in the model matrix
1380 obj->bounding_radius = radius_x;
1381 if (obj->bounding_radius < radius_y)
1382 obj->bounding_radius = radius_y;
1383 if (obj->bounding_radius < radius_z)
1384 obj->bounding_radius = radius_z;
1385
1386 for (unsigned int i = 0; i < obj->points.size(); i += 3) {
1387 obj->points[i] -= obj->bounding_center.x;
1388 obj->points[i + 1] -= obj->bounding_center.y;
1389 obj->points[i + 2] -= obj->bounding_center.z;
1390 }
1391
1392 obj->bounding_center = vec3(0.0f, 0.0f, 0.0f);
1393}
1394
1395SceneObject* createShip(GLuint shader) {
1396 SceneObject* ship = new SceneObject();
1397
1398 ship->type = TYPE_SHIP;
1399 ship->shader_program = shader;
1400
1401 ship->points = {
1402 //back
1403 -0.5f, 0.3f, 0.0f,
1404 -0.5f, 0.0f, 0.0f,
1405 0.5f, 0.0f, 0.0f,
1406 -0.5f, 0.3f, 0.0f,
1407 0.5f, 0.0f, 0.0f,
1408 0.5f, 0.3f, 0.0f,
1409
1410 // left back
1411 -0.5f, 0.3f, -2.0f,
1412 -0.5f, 0.0f, -2.0f,
1413 -0.5f, 0.0f, 0.0f,
1414 -0.5f, 0.3f, -2.0f,
1415 -0.5f, 0.0f, 0.0f,
1416 -0.5f, 0.3f, 0.0f,
1417
1418 // right back
1419 0.5f, 0.3f, 0.0f,
1420 0.5f, 0.0f, 0.0f,
1421 0.5f, 0.0f, -2.0f,
1422 0.5f, 0.3f, 0.0f,
1423 0.5f, 0.0f, -2.0f,
1424 0.5f, 0.3f, -2.0f,
1425
1426 // left mid
1427 -0.25f, 0.3f, -3.0f,
1428 -0.25f, 0.0f, -3.0f,
1429 -0.5f, 0.0f, -2.0f,
1430 -0.25f, 0.3f, -3.0f,
1431 -0.5f, 0.0f, -2.0f,
1432 -0.5f, 0.3f, -2.0f,
1433
1434 // right mid
1435 0.5f, 0.3f, -2.0f,
1436 0.5f, 0.0f, -2.0f,
1437 0.25f, 0.0f, -3.0f,
1438 0.5f, 0.3f, -2.0f,
1439 0.25f, 0.0f, -3.0f,
1440 0.25f, 0.3f, -3.0f,
1441
1442 // left front
1443 0.0f, 0.0f, -3.5f,
1444 -0.25f, 0.0f, -3.0f,
1445 -0.25f, 0.3f, -3.0f,
1446
1447 // right front
1448 0.25f, 0.3f, -3.0f,
1449 0.25f, 0.0f, -3.0f,
1450 0.0f, 0.0f, -3.5f,
1451
1452 // top back
1453 -0.5f, 0.3f, -2.0f,
1454 -0.5f, 0.3f, 0.0f,
1455 0.5f, 0.3f, 0.0f,
1456 -0.5f, 0.3f, -2.0f,
1457 0.5f, 0.3f, 0.0f,
1458 0.5f, 0.3f, -2.0f,
1459
1460 // bottom back
1461 -0.5f, 0.0f, 0.0f,
1462 -0.5f, 0.0f, -2.0f,
1463 0.5f, 0.0f, 0.0f,
1464 0.5f, 0.0f, 0.0f,
1465 -0.5f, 0.0f, -2.0f,
1466 0.5f, 0.0f, -2.0f,
1467
1468 // top mid
1469 -0.25f, 0.3f, -3.0f,
1470 -0.5f, 0.3f, -2.0f,
1471 0.5f, 0.3f, -2.0f,
1472 -0.25f, 0.3f, -3.0f,
1473 0.5f, 0.3f, -2.0f,
1474 0.25f, 0.3f, -3.0f,
1475
1476 // bottom mid
1477 -0.5f, 0.0f, -2.0f,
1478 -0.25f, 0.0f, -3.0f,
1479 0.5f, 0.0f, -2.0f,
1480 0.5f, 0.0f, -2.0f,
1481 -0.25f, 0.0f, -3.0f,
1482 0.25f, 0.0f, -3.0f,
1483
1484 // top front
1485 -0.25f, 0.3f, -3.0f,
1486 0.25f, 0.3f, -3.0f,
1487 0.0f, 0.0f, -3.5f,
1488
1489 // bottom front
1490 0.25f, 0.0f, -3.0f,
1491 -0.25f, 0.0f, -3.0f,
1492 0.0f, 0.0f, -3.5f,
1493
1494 // left wing start back
1495 -1.5f, 0.3f, 0.0f,
1496 -1.5f, 0.0f, 0.0f,
1497 -0.5f, 0.0f, 0.0f,
1498 -1.5f, 0.3f, 0.0f,
1499 -0.5f, 0.0f, 0.0f,
1500 -0.5f, 0.3f, 0.0f,
1501
1502 // left wing start top
1503 -0.5f, 0.3f, -0.3f,
1504 -1.3f, 0.3f, -0.3f,
1505 -1.5f, 0.3f, 0.0f,
1506 -0.5f, 0.3f, -0.3f,
1507 -1.5f, 0.3f, 0.0f,
1508 -0.5f, 0.3f, 0.0f,
1509
1510 // left wing start front
1511 -0.5f, 0.3f, -0.3f,
1512 -0.5f, 0.0f, -0.3f,
1513 -1.3f, 0.0f, -0.3f,
1514 -0.5f, 0.3f, -0.3f,
1515 -1.3f, 0.0f, -0.3f,
1516 -1.3f, 0.3f, -0.3f,
1517
1518 // left wing start bottom
1519 -0.5f, 0.0f, 0.0f,
1520 -1.5f, 0.0f, 0.0f,
1521 -1.3f, 0.0f, -0.3f,
1522 -0.5f, 0.0f, 0.0f,
1523 -1.3f, 0.0f, -0.3f,
1524 -0.5f, 0.0f, -0.3f,
1525
1526 // left wing end outside
1527 -1.5f, 0.3f, 0.0f,
1528 -2.2f, 0.15f, -0.8f,
1529 -1.5f, 0.0f, 0.0f,
1530
1531 // left wing end top
1532 -1.3f, 0.3f, -0.3f,
1533 -2.2f, 0.15f, -0.8f,
1534 -1.5f, 0.3f, 0.0f,
1535
1536 // left wing end front
1537 -1.3f, 0.0f, -0.3f,
1538 -2.2f, 0.15f, -0.8f,
1539 -1.3f, 0.3f, -0.3f,
1540
1541 // left wing end bottom
1542 -1.5f, 0.0f, 0.0f,
1543 -2.2f, 0.15f, -0.8f,
1544 -1.3f, 0.0f, -0.3f,
1545
1546 // right wing start back
1547 1.5f, 0.0f, 0.0f,
1548 1.5f, 0.3f, 0.0f,
1549 0.5f, 0.0f, 0.0f,
1550 0.5f, 0.0f, 0.0f,
1551 1.5f, 0.3f, 0.0f,
1552 0.5f, 0.3f, 0.0f,
1553
1554 // right wing start top
1555 1.3f, 0.3f, -0.3f,
1556 0.5f, 0.3f, -0.3f,
1557 1.5f, 0.3f, 0.0f,
1558 1.5f, 0.3f, 0.0f,
1559 0.5f, 0.3f, -0.3f,
1560 0.5f, 0.3f, 0.0f,
1561
1562 // right wing start front
1563 0.5f, 0.0f, -0.3f,
1564 0.5f, 0.3f, -0.3f,
1565 1.3f, 0.0f, -0.3f,
1566 1.3f, 0.0f, -0.3f,
1567 0.5f, 0.3f, -0.3f,
1568 1.3f, 0.3f, -0.3f,
1569
1570 // right wing start bottom
1571 1.5f, 0.0f, 0.0f,
1572 0.5f, 0.0f, 0.0f,
1573 1.3f, 0.0f, -0.3f,
1574 1.3f, 0.0f, -0.3f,
1575 0.5f, 0.0f, 0.0f,
1576 0.5f, 0.0f, -0.3f,
1577
1578 // right wing end outside
1579 2.2f, 0.15f, -0.8f,
1580 1.5f, 0.3f, 0.0f,
1581 1.5f, 0.0f, 0.0f,
1582
1583 // right wing end top
1584 2.2f, 0.15f, -0.8f,
1585 1.3f, 0.3f, -0.3f,
1586 1.5f, 0.3f, 0.0f,
1587
1588 // right wing end front
1589 2.2f, 0.15f, -0.8f,
1590 1.3f, 0.0f, -0.3f,
1591 1.3f, 0.3f, -0.3f,
1592
1593 // right wing end bottom
1594 2.2f, 0.15f, -0.8f,
1595 1.5f, 0.0f, 0.0f,
1596 1.3f, 0.0f, -0.3f,
1597 };
1598 ship->colors = {
1599 0.0f, 0.0f, 0.3f,
1600 0.0f, 0.0f, 0.3f,
1601 0.0f, 0.0f, 0.3f,
1602 0.0f, 0.0f, 0.3f,
1603 0.0f, 0.0f, 0.3f,
1604 0.0f, 0.0f, 0.3f,
1605
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, 1.0f,
1635 0.0f, 0.0f, 1.0f,
1636 0.0f, 0.0f, 1.0f,
1637
1638 0.0f, 0.0f, 1.0f,
1639 0.0f, 0.0f, 1.0f,
1640 0.0f, 0.0f, 1.0f,
1641
1642 0.0f, 0.0f, 1.0f,
1643 0.0f, 0.0f, 1.0f,
1644 0.0f, 0.0f, 1.0f,
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, 0.3f,
1671 0.0f, 0.0f, 0.3f,
1672 0.0f, 0.0f, 0.3f,
1673
1674 0.0f, 0.0f, 0.3f,
1675 0.0f, 0.0f, 0.3f,
1676 0.0f, 0.0f, 0.3f,
1677
1678 0.0f, 0.0f, 0.3f,
1679 0.0f, 0.0f, 0.3f,
1680 0.0f, 0.0f, 0.3f,
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
1710 0.0f, 0.0f, 0.3f,
1711 0.0f, 0.0f, 0.3f,
1712 0.0f, 0.0f, 0.3f,
1713
1714 0.0f, 0.0f, 0.3f,
1715 0.0f, 0.0f, 0.3f,
1716 0.0f, 0.0f, 0.3f,
1717
1718 0.0f, 0.0f, 0.3f,
1719 0.0f, 0.0f, 0.3f,
1720 0.0f, 0.0f, 0.3f,
1721
1722 0.0f, 0.0f, 0.3f,
1723 0.0f, 0.0f, 0.3f,
1724 0.0f, 0.0f, 0.3f,
1725 0.0f, 0.0f, 0.3f,
1726 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
1754 0.0f, 0.0f, 0.3f,
1755 0.0f, 0.0f, 0.3f,
1756 0.0f, 0.0f, 0.3f,
1757
1758 0.0f, 0.0f, 0.3f,
1759 0.0f, 0.0f, 0.3f,
1760 0.0f, 0.0f, 0.3f,
1761
1762 0.0f, 0.0f, 0.3f,
1763 0.0f, 0.0f, 0.3f,
1764 0.0f, 0.0f, 0.3f,
1765 };
1766 ship->texcoords = { 0.0f };
1767 ship->selected_colors = { 0.0f };
1768
1769 mat4 T_model = translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f));
1770 mat4 R_model(1.0f);
1771 ship->model_base = T_model * R_model * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
1772
1773 ship->translate_mat = T_model;
1774
1775 initObject(ship);
1776
1777 return ship;
1778}
1779
1780/* LASER RENDERING/POSITIONING ALGORITHM
1781 * -Draw a thin rectangle for the laser beam, using the specified width and endpoints
1782 * -Texture the beam with a grayscale partially transparent image
1783 * -In the shader, blend the image with a color to support lasers of different colors
1784 *
1785 * The flat part of the textured rectangle needs to always face the camera, so the laser's width is constant
1786 * This is done as follows:
1787* -Determine the length of the laser based on the start and end points
1788* -Draw a rectangle along the z-axis and rotated upwards along the y-axis, with the correct final length and width
1789* -Rotate the beam around the z-axis by the correct angle, sot that in its final position, the flat part faces the camera
1790* -Rotate the beam along the x-axis and then along the y-axis and then translate it to put it into its final position
1791*/
1792// TODO: Make the color parameter have an effect
1793// TODO: Come up with a better way of passing the object back than copying it
1794Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width, GLuint laser_sp) {
1795 Laser* obj = new Laser();
1796 obj->type = TYPE_LASER;
1797 obj->targetAsteroid = NULL;
1798 obj->shader_program = laser_sp;
1799
1800 vec3 ray = end - start;
1801 float length = glm::length(ray);
1802
1803 obj->points = {
1804 width / 2, 0.0f, -width / 2,
1805 -width / 2, 0.0f, -width / 2,
1806 -width / 2, 0.0f, 0.0f,
1807 width / 2, 0.0f, -width / 2,
1808 -width / 2, 0.0f, 0.0f,
1809 width / 2, 0.0f, 0.0f,
1810 width / 2, 0.0f, -length + width / 2,
1811 -width / 2, 0.0f, -length + width / 2,
1812 -width / 2, 0.0f, -width / 2,
1813 width / 2, 0.0f, -length + width / 2,
1814 -width / 2, 0.0f, -width / 2,
1815 width / 2, 0.0f, -width / 2,
1816 width / 2, 0.0f, -length,
1817 -width / 2, 0.0f, -length,
1818 -width / 2, 0.0f, -length + width / 2,
1819 width / 2, 0.0f, -length,
1820 -width / 2, 0.0f, -length + width / 2,
1821 width / 2, 0.0f, -length + width / 2,
1822 };
1823
1824 obj->texcoords = {
1825 1.0f, 0.5f,
1826 0.0f, 0.5f,
1827 0.0f, 0.0f,
1828 1.0f, 0.5f,
1829 0.0f, 0.0f,
1830 1.0f, 0.0f,
1831 1.0f, 0.51f,
1832 0.0f, 0.51f,
1833 0.0f, 0.49f,
1834 1.0f, 0.51f,
1835 0.0f, 0.49f,
1836 1.0f, 0.49f,
1837 1.0f, 1.0f,
1838 0.0f, 1.0f,
1839 0.0f, 0.5f,
1840 1.0f, 1.0f,
1841 0.0f, 0.5f,
1842 1.0f, 0.5f,
1843 };
1844
1845 float xAxisRotation = asin(ray.y / length);
1846 float yAxisRotation = atan2(-ray.x, -ray.z);
1847
1848 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1849 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1850 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1851
1852 // To project point P onto line AB:
1853 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1854 vec3 projOnLaser = start + glm::dot(cam_pos-start, ray) / (length*length) * ray;
1855 vec3 laserToCam = cam_pos - projOnLaser;
1856
1857 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1858
1859 obj->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1860
1861 initObject(obj);
1862
1863 obj->model_transform = rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) * obj->model_transform;
1864 obj->model_transform = rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) * obj->model_transform;
1865 obj->model_transform = translate(mat4(1.0f), start) * obj->model_transform;
1866
1867 return obj;
1868}
1869
1870void initializeBuffers(
1871 GLuint* points_vbo,
1872 GLuint* colors_vbo,
1873 GLuint* selected_colors_vbo,
1874 GLuint* texcoords_vbo,
1875 GLuint* normals_vbo,
1876 GLuint* ubo,
1877 GLuint* model_mat_idx_vbo) {
1878 *points_vbo = 0;
1879 glGenBuffers(1, points_vbo);
1880
1881 *colors_vbo = 0;
1882 glGenBuffers(1, colors_vbo);
1883
1884 *selected_colors_vbo = 0;
1885 glGenBuffers(1, selected_colors_vbo);
1886
1887 *texcoords_vbo = 0;
1888 glGenBuffers(1, texcoords_vbo);
1889
1890 *normals_vbo = 0;
1891 glGenBuffers(1, normals_vbo);
1892
1893 *ubo = 0;
1894 glGenBuffers(1, ubo);
1895
1896 *model_mat_idx_vbo = 0;
1897 glGenBuffers(1, model_mat_idx_vbo);
1898}
1899
1900GLuint initializeParticleEffectBuffers() {
1901 float vv[EXPLOSION_PARTICLE_COUNT * 3]; // initial velocities vec3
1902 float vt[EXPLOSION_PARTICLE_COUNT]; // initial times
1903 float t_accum = 0.0f; // start time
1904
1905 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
1906 vt[i] = t_accum;
1907 t_accum += 0.01f;
1908
1909 float randx = ((float)rand() / (float)RAND_MAX) * 1.0f - 0.5f;
1910 float randz = ((float)rand() / (float)RAND_MAX) * 1.0f - 0.5f;
1911 vv[i*3] = randx;
1912 vv[i*3 + 1] = 1.0f;
1913 vv[i*3 + 2] = randz;
1914 }
1915
1916 GLuint velocity_vbo;
1917 glGenBuffers(1, &velocity_vbo);
1918 glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
1919 glBufferData(GL_ARRAY_BUFFER, sizeof(vv), vv, GL_STATIC_DRAW);
1920
1921 GLuint time_vbo;
1922 glGenBuffers(1, &time_vbo);
1923 glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
1924 glBufferData(GL_ARRAY_BUFFER, sizeof(vt), vt, GL_STATIC_DRAW);
1925
1926 GLuint vao;
1927 glGenVertexArrays(1, &vao);
1928 glBindVertexArray(vao);
1929
1930 glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
1931 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
1932
1933 glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
1934 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, NULL);
1935
1936 glEnableVertexAttribArray(0);
1937 glEnableVertexAttribArray(1);
1938
1939 return vao;
1940}
1941
1942void populateBuffers(vector<SceneObject*>& objects,
1943 map<GLuint, BufferInfo>& shaderBufferInfo,
1944 GLuint points_vbo,
1945 GLuint colors_vbo,
1946 GLuint selected_colors_vbo,
1947 GLuint texcoords_vbo,
1948 GLuint normals_vbo,
1949 GLuint ubo,
1950 GLuint ubo_idx_vbo,
1951 GLuint asteroid_sp) {
1952 GLsizeiptr num_points = 0;
1953 GLsizeiptr num_objects = 0;
1954
1955 map<GLuint, unsigned int> shaderCounts;
1956 map<GLuint, unsigned int> shaderUboCounts;
1957
1958 vector<SceneObject*>::iterator it;
1959
1960 /* Find all shaders that need to be used and the number of objects and
1961 * number of points for each shader. Construct a map from shader id to count
1962 * of points being drawn using that shader (for thw model matrix ubo, we
1963 * need object counts instead). These will be used to get offsets into the
1964 * vertex buffer for each shader.
1965 */
1966 for (it = objects.begin(); it != objects.end(); ) {
1967 if ((*it)->deleted) {
1968 delete *it;
1969 it = objects.erase(it);
1970 } else {
1971 num_points += (*it)->num_points;
1972 num_objects++;
1973
1974 if (shaderCounts.count((*it)->shader_program) == 0) {
1975 shaderCounts[(*it)->shader_program] = (*it)->num_points;
1976 shaderUboCounts[(*it)->shader_program] = 1;
1977 } else {
1978 shaderCounts[(*it)->shader_program] += (*it)->num_points;
1979 shaderUboCounts[(*it)->shader_program]++;
1980 }
1981
1982 it++;
1983 }
1984 }
1985
1986 // double the buffer sizes to leave room for new objects
1987 num_points *= 2;
1988 num_objects *= 2;
1989
1990 map<GLuint, unsigned int>::iterator shaderIt;
1991 unsigned int lastShaderCount = 0;
1992 unsigned int lastShaderUboCount = 0;
1993
1994 /*
1995 * The counts calculated above can be used to get the starting offset of
1996 * each shader in the vertex buffer. Create a map of base offsets to mark
1997 * where the data for the first object using a given shader begins. Also,
1998 * create a map of current offsets to mark where to copy data for the next
1999 * object being added.
2000 */
2001 for (shaderIt = shaderCounts.begin(); shaderIt != shaderCounts.end(); shaderIt++) {
2002 // When populating the buffers, leave as much empty space as space taken up by existing objects
2003 // to allow new objects to be added without immediately having to resize the buffers
2004 shaderBufferInfo[shaderIt->first].vbo_base = lastShaderCount * 2;
2005 shaderBufferInfo[shaderIt->first].ubo_base = lastShaderUboCount * 2;
2006
2007 /*
2008 cout << "shader: " << shaderIt->first << endl;
2009 cout << "point counts: " << shaderCounts[shaderIt->first] << endl;
2010 cout << "object counts: " << shaderUboCounts[shaderIt->first] << endl;
2011 cout << "vbo_base: " << shaderBufferInfo[shaderIt->first].vbo_base << endl;
2012 cout << "ubo_base: " << shaderBufferInfo[shaderIt->first].ubo_base << endl;
2013 */
2014
2015 shaderBufferInfo[shaderIt->first].vbo_offset = 0;
2016 shaderBufferInfo[shaderIt->first].ubo_offset = 0;
2017
2018 shaderBufferInfo[shaderIt->first].vbo_capacity = shaderCounts[shaderIt->first] * 2;
2019 shaderBufferInfo[shaderIt->first].ubo_capacity = shaderUboCounts[shaderIt->first] * 2;
2020
2021 lastShaderCount += shaderCounts[shaderIt->first];
2022 lastShaderUboCount += shaderUboCounts[shaderIt->first];
2023 }
2024
2025 // Allocate all the buffers using the counts calculated above
2026
2027 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2028 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2029
2030 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
2031 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2032
2033 glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
2034 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2035
2036 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
2037 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 2, NULL, GL_DYNAMIC_DRAW);
2038
2039 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
2040 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
2041
2042 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2043 glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
2044
2045 glBindBuffer(GL_ARRAY_BUFFER, ubo_idx_vbo);
2046 glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);
2047
2048 for (it = objects.begin(); it != objects.end(); it++) {
2049 copyObjectDataToBuffers(**it, shaderBufferInfo,
2050 points_vbo,
2051 colors_vbo,
2052 selected_colors_vbo,
2053 texcoords_vbo,
2054 normals_vbo,
2055 ubo,
2056 ubo_idx_vbo,
2057 asteroid_sp);
2058 }
2059}
2060
2061void copyObjectDataToBuffers(SceneObject& obj,
2062 map<GLuint, BufferInfo>& shaderBufferInfo,
2063 GLuint points_vbo,
2064 GLuint colors_vbo,
2065 GLuint selected_colors_vbo,
2066 GLuint texcoords_vbo,
2067 GLuint normals_vbo,
2068 GLuint ubo,
2069 GLuint model_mat_idx_vbo,
2070 GLuint asteroid_sp) {
2071 BufferInfo* bufferInfo = &shaderBufferInfo[obj.shader_program];
2072
2073 obj.vertex_vbo_offset = bufferInfo->vbo_base + bufferInfo->vbo_offset;
2074 obj.ubo_offset = bufferInfo->ubo_base + bufferInfo->ubo_offset;
2075
2076 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2077 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.points.size() * sizeof(GLfloat), &obj.points[0]);
2078
2079 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
2080 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 2, obj.texcoords.size() * sizeof(GLfloat), &obj.texcoords[0]);
2081
2082 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
2083 for (unsigned int i = 0; i < obj.num_points; i++) {
2084 glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * sizeof(GLuint), sizeof(GLuint), &obj.ubo_offset);
2085 }
2086
2087 if (obj.type != TYPE_LASER) {
2088 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
2089 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.colors.size() * sizeof(GLfloat), &obj.colors[0]);
2090
2091 glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
2092 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.selected_colors.size() * sizeof(GLfloat), &obj.selected_colors[0]);
2093
2094 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
2095 glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.normals.size() * sizeof(GLfloat), &obj.normals[0]);
2096 }
2097
2098 obj.model_mat = obj.model_transform * obj.model_base;
2099 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2100 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2101
2102 if (obj.type == TYPE_ASTEROID) {
2103 glUseProgram(asteroid_sp);
2104
2105 ostringstream oss;
2106 oss << "hp[" << obj.ubo_offset << "]";
2107 glUniform1f(glGetUniformLocation(asteroid_sp, oss.str().c_str()), ((Asteroid*)&obj)->hp);
2108 }
2109
2110 bufferInfo->vbo_offset += obj.num_points;
2111 bufferInfo->ubo_offset++;
2112}
2113
2114void transformObject(SceneObject& obj, const mat4& transform, GLuint ubo) {
2115 if (obj.deleted) return;
2116
2117 obj.model_transform = transform * obj.model_transform;
2118 obj.model_mat = obj.model_transform * obj.model_base;
2119
2120 obj.bounding_center = vec3(transform * vec4(obj.bounding_center, 1.0f));
2121
2122 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
2123 glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
2124}
2125
2126void translateLaser(Laser* laser, const vec3& translation, GLuint ubo) {
2127 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2128 // and then re-used here
2129
2130 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2131 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[38], 1.0f));
2132
2133 vec3 ray = end - start;
2134 float length = glm::length(ray);
2135
2136 float xAxisRotation = asin(ray.y / length);
2137 float yAxisRotation = atan2(-ray.x, -ray.z);
2138
2139 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2140 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2141 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2142
2143 // To project point P onto line AB:
2144 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2145 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
2146 vec3 laserToCam = cam_pos - projOnLaser;
2147
2148 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2149
2150 laser->model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2151
2152 transformObject(*laser, translate(mat4(1.0f), translation), ubo);
2153}
2154
2155void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp) {
2156 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2157 // and then re-used here
2158
2159 vec3 start = vec3(laser->model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2160 vec3 end = vec3(laser->model_transform * vec4(0.0f, 0.0f, laser->points[2] + laser->points[20], 1.0f));
2161
2162 vec3 intersection(0.0f), closestIntersection(0.0f);
2163 Asteroid* closestAsteroid = NULL;
2164
2165 for (vector<SceneObject*>::iterator it = objects.begin(); it != objects.end(); it++) {
2166 if ((*it)->type == TYPE_ASTEROID && !(*it)->deleted && getLaserAndAsteroidIntersection(start, end, **it, intersection)) {
2167 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2168 if (closestAsteroid == NULL || intersection.z > closestIntersection.z) {
2169 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2170 closestAsteroid = (Asteroid*)*it;
2171 closestIntersection = intersection;
2172 }
2173 }
2174 }
2175
2176 float width = laser->points[0] - laser->points[2];
2177
2178 if (laser->targetAsteroid != closestAsteroid) {
2179 if (laser->targetAsteroid != NULL) {
2180 if (laser == leftLaser) {
2181 leftLaserEffect->deleted = true;
2182 } else if (laser == rightLaser) {
2183 rightLaserEffect->deleted = true;
2184 }
2185 }
2186
2187 EffectOverTime* eot = NULL;
2188
2189 if (closestAsteroid != NULL) {
2190 eot = new EffectOverTime(closestAsteroid->hp, -20.0f, closestAsteroid);
2191 effects.push_back(eot);
2192 }
2193
2194 if (laser == leftLaser) {
2195 leftLaserEffect = eot;
2196 } else if (laser == rightLaser) {
2197 rightLaserEffect = eot;
2198 }
2199 }
2200 laser->targetAsteroid = closestAsteroid;
2201
2202 float length = 5.24f; // I think this was to make sure the laser went past the end of the screen
2203 if (closestAsteroid != NULL) {
2204 length = glm::length(closestIntersection - start);
2205
2206 // TODO: Find a more generic way of updating the laser hp than in updateLaserTarget
2207
2208 glUseProgram(asteroid_sp);
2209
2210 ostringstream oss;
2211 oss << "hp[" << closestAsteroid->ubo_offset << "]";
2212 glUniform1f(glGetUniformLocation(asteroid_sp, oss.str().c_str()), closestAsteroid->hp);
2213 }
2214
2215 laser->points[20] = -length + width / 2;
2216 laser->points[23] = -length + width / 2;
2217 laser->points[29] = -length + width / 2;
2218 laser->points[38] = -length;
2219 laser->points[41] = -length;
2220 laser->points[44] = -length + width / 2;
2221 laser->points[47] = -length;
2222 laser->points[50] = -length + width / 2;
2223 laser->points[53] = -length + width / 2;
2224
2225 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
2226 glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * sizeof(GLfloat) * 3, laser->points.size() * sizeof(GLfloat), &laser->points[0]);
2227}
2228
2229bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection) {
2230 /*
2231 ### LINE EQUATIONS ###
2232 x = x1 + u * (x2 - x1)
2233 y = y1 + u * (y2 - y1)
2234 z = z1 + u * (z2 - z1)
2235
2236 ### SPHERE EQUATION ###
2237 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2238
2239 ### QUADRATIC EQUATION TO SOLVE ###
2240 a*u^2 + b*u + c = 0
2241 WHERE THE CONSTANTS ARE
2242 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2243 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2244 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2245
2246 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2247
2248 If the value under the root is >= 0, we got an intersection
2249 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2250 one closer to the laser start point
2251 */
2252
2253 vec3& center = asteroid.bounding_center;
2254
2255 float a = pow(end.x-start.x, 2) + pow(end.y-start.y, 2) + pow(end.z-start.z, 2);
2256 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));
2257 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);
2258 float discriminant = pow(b, 2) - 4*a*c;
2259
2260 if (discriminant >= 0.0f) {
2261 // In this case, the negative root will always give the point closer to the laser start point
2262 float u = (-b - sqrt(discriminant)) / (2 * a);
2263
2264 // Check that the intersection is within the line segment corresponding to the laser
2265 if (0.0f <= u && u <= 1.0f) {
2266 intersection = start + u * (end - start);
2267 return true;
2268 }
2269 }
2270
2271 return false;
2272}
2273
2274void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
2275 GLuint color_sp, GLuint asteroid_sp, GLuint texture_sp, GLuint laser_sp, GLuint explosion_sp,
2276 GLuint color_vao, GLuint asteroid_vao, GLuint texture_vao, GLuint laser_vao, GLuint explosion_vao,
2277 GLuint colors_vbo, GLuint selected_colors_vbo,
2278 SceneObject* selectedObject) {
2279
2280 glUseProgram(color_sp);
2281 glBindVertexArray(color_vao);
2282
2283 /*
2284 if (selectedObject != NULL) {
2285 glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
2286 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
2287
2288 glDrawArrays(GL_TRIANGLES, selectedObject->vertex_vbo_offset, selectedObject->num_points);
2289 }
2290 */
2291
2292 // Uncomment this code when I want to use selected colors again
2293 // glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
2294 // glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
2295
2296 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[color_sp].vbo_base, shaderBufferInfo[color_sp].vbo_offset);
2297
2298 glUseProgram(asteroid_sp);
2299 glBindVertexArray(asteroid_vao);
2300
2301 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[asteroid_sp].vbo_base, shaderBufferInfo[asteroid_sp].vbo_offset);
2302
2303 glUseProgram(texture_sp);
2304 glBindVertexArray(texture_vao);
2305
2306 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[texture_sp].vbo_base, shaderBufferInfo[texture_sp].vbo_offset);
2307
2308 glEnable(GL_BLEND);
2309
2310 glUseProgram(laser_sp);
2311 glBindVertexArray(laser_vao);
2312
2313 glDrawArrays(GL_TRIANGLES, shaderBufferInfo[laser_sp].vbo_base, shaderBufferInfo[laser_sp].vbo_offset);
2314
2315 glDisable(GL_BLEND);
2316
2317 renderExplosion(explosion_sp, explosion_vao, 0);
2318}
2319
2320void renderExplosion(GLuint explosion_sp, GLuint explosion_vao, GLuint tex) {
2321 glEnable(GL_BLEND);
2322 glEnable(GL_PROGRAM_POINT_SIZE);
2323
2324 //glActiveTexture(GL_TEXTURE1);
2325 //glBindTexture(GL_TEXTURE_2D, tex);
2326 glUseProgram(explosion_sp);
2327
2328 glBindVertexArray(explosion_vao);
2329
2330 glDrawArrays(GL_POINTS, 0, EXPLOSION_PARTICLE_COUNT);
2331
2332 glDisable(GL_PROGRAM_POINT_SIZE);
2333 glDisable(GL_BLEND);
2334}
2335
2336void renderSceneGui() {
2337 ImGui_ImplGlfwGL3_NewFrame();
2338
2339 // 1. Show a simple window.
2340 // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
2341 /*
2342 {
2343 static float f = 0.0f;
2344 static int counter = 0;
2345 ImGui::Text("Hello, world!"); // Display some text (you can use a format string too)
2346 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
2347 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
2348
2349 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state
2350 ImGui::Checkbox("Another Window", &show_another_window);
2351
2352 if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated)
2353 counter++;
2354 ImGui::SameLine();
2355 ImGui::Text("counter = %d", counter);
2356
2357 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
2358 }
2359 */
2360
2361 stringstream ssScore, ssFps;
2362 ssScore << "Score: " << score;
2363 ssFps << "FPS: " << fps;
2364
2365 {
2366 ImGui::SetNextWindowSize(ImVec2(95, 46), ImGuiCond_Once);
2367 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
2368 ImGui::Begin("WndStats", NULL,
2369 ImGuiWindowFlags_NoTitleBar |
2370 ImGuiWindowFlags_NoResize |
2371 ImGuiWindowFlags_NoMove);
2372 ImGui::Text(ssScore.str().c_str());
2373 ImGui::Text(ssFps.str().c_str());
2374 ImGui::End();
2375 }
2376
2377 {
2378 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
2379 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
2380 ImGui::Begin("WndMenubar", NULL,
2381 ImGuiWindowFlags_NoTitleBar |
2382 ImGuiWindowFlags_NoResize |
2383 ImGuiWindowFlags_NoMove);
2384 ImGui::InvisibleButton("", ImVec2(155, 18));
2385 ImGui::SameLine();
2386 if (ImGui::Button("Main Menu")) {
2387 events.push(EVENT_GO_TO_MAIN_MENU);
2388 }
2389 ImGui::End();
2390 }
2391
2392 ImGui::Render();
2393 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2394}
2395
2396void renderMainMenu() {
2397}
2398
2399void renderMainMenuGui() {
2400 ImGui_ImplGlfwGL3_NewFrame();
2401
2402 {
2403 int padding = 4;
2404 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
2405 ImGui::SetNextWindowSize(ImVec2(width + 2 * padding, height + 2 * padding), ImGuiCond_Once);
2406 ImGui::Begin("WndMain", NULL,
2407 ImGuiWindowFlags_NoTitleBar |
2408 ImGuiWindowFlags_NoResize |
2409 ImGuiWindowFlags_NoMove);
2410
2411 ImGui::InvisibleButton("", ImVec2(10, 80));
2412 ImGui::InvisibleButton("", ImVec2(285, 18));
2413 ImGui::SameLine();
2414 if (ImGui::Button("New Game")) {
2415 events.push(EVENT_GO_TO_GAME);
2416 }
2417
2418 ImGui::InvisibleButton("", ImVec2(10, 15));
2419 ImGui::InvisibleButton("", ImVec2(300, 18));
2420 ImGui::SameLine();
2421 if (ImGui::Button("Quit")) {
2422 events.push(EVENT_QUIT);
2423 }
2424
2425 ImGui::End();
2426 }
2427
2428 ImGui::Render();
2429 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
2430}
2431
2432Asteroid* createAsteroid(vec3 pos, GLuint shader) {
2433 Asteroid* obj = new Asteroid();
2434 obj->type = TYPE_ASTEROID;
2435 obj->hp = 10.0f;
2436 obj->shader_program = shader;
2437
2438 obj->points = {
2439 // front
2440 1.0f, 1.0f, 1.0f,
2441 -1.0f, 1.0f, 1.0f,
2442 -1.0f, -1.0f, 1.0f,
2443 1.0f, 1.0f, 1.0f,
2444 -1.0f, -1.0f, 1.0f,
2445 1.0f, -1.0f, 1.0f,
2446
2447 // top
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 1.0f, 1.0f, 1.0f,
2454
2455 // bottom
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 1.0f, -1.0f, -1.0f,
2462
2463 // back
2464 1.0f, 1.0f, -1.0f,
2465 -1.0f, -1.0f, -1.0f,
2466 -1.0f, 1.0f, -1.0f,
2467 1.0f, 1.0f, -1.0f,
2468 1.0f, -1.0f, -1.0f,
2469 -1.0f, -1.0f, -1.0f,
2470
2471 // right
2472 1.0f, 1.0f, -1.0f,
2473 1.0f, 1.0f, 1.0f,
2474 1.0f, -1.0f, 1.0f,
2475 1.0f, 1.0f, -1.0f,
2476 1.0f, -1.0f, 1.0f,
2477 1.0f, -1.0f, -1.0f,
2478
2479 // left
2480 -1.0f, 1.0f, 1.0f,
2481 -1.0f, 1.0f, -1.0f,
2482 -1.0f, -1.0f, -1.0f,
2483 -1.0f, 1.0f, 1.0f,
2484 -1.0f, -1.0f, -1.0f,
2485 -1.0f, -1.0f, 1.0f,
2486 };
2487 obj->colors = {
2488 // front
2489 0.4f, 0.4f, 0.4f,
2490 0.4f, 0.4f, 0.4f,
2491 0.4f, 0.4f, 0.4f,
2492 0.4f, 0.4f, 0.4f,
2493 0.4f, 0.4f, 0.4f,
2494 0.4f, 0.4f, 0.4f,
2495
2496 // top
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 0.4f, 0.4f, 0.4f,
2503
2504 // bottom
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 0.4f, 0.4f, 0.4f,
2511
2512 // back
2513 0.4f, 0.4f, 0.4f,
2514 0.4f, 0.4f, 0.4f,
2515 0.4f, 0.4f, 0.4f,
2516 0.4f, 0.4f, 0.4f,
2517 0.4f, 0.4f, 0.4f,
2518 0.4f, 0.4f, 0.4f,
2519
2520 // right
2521 0.4f, 0.4f, 0.4f,
2522 0.4f, 0.4f, 0.4f,
2523 0.4f, 0.4f, 0.4f,
2524 0.4f, 0.4f, 0.4f,
2525 0.4f, 0.4f, 0.4f,
2526 0.4f, 0.4f, 0.4f,
2527
2528 // left
2529 0.4f, 0.4f, 0.4f,
2530 0.4f, 0.4f, 0.4f,
2531 0.4f, 0.4f, 0.4f,
2532 0.4f, 0.4f, 0.4f,
2533 0.4f, 0.4f, 0.4f,
2534 0.4f, 0.4f, 0.4f,
2535 };
2536 obj->texcoords = { 0.0f };
2537 obj->selected_colors = { 0.0f };
2538
2539 mat4 T = translate(mat4(1.0f), pos);
2540 mat4 R = rotate(mat4(1.0f), 60.0f * (float)ONE_DEG_IN_RAD, vec3(1.0f, 1.0f, -1.0f));
2541 obj->model_base = T * R * scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
2542
2543 obj->translate_mat = T;
2544
2545 initObject(obj);
2546 // This accounts for the scaling in model_base.
2547 // Dividing by 8 instead of 10 since the bounding radius algorithm
2548 // under-calculates the true value.
2549 // TODO: Once the intersection check with the sides of the asteroid is done,
2550 // this can be removed.
2551 obj->bounding_radius /= 8.0f;
2552
2553 return obj;
2554}
2555
2556float getRandomNum(float low, float high) {
2557 return low + ((float)rand()/RAND_MAX) * (high-low);
2558}
Note: See TracBrowser for help on using the repository browser.