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

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

Fix some g++ compiler warnings

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