source: opengl-game/new-game.cpp@ 446e55d

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

Fix the OpenGL version issue on OSX by making it only request an OpenGL 3.3 context, since OSX doesn't support OpenGL 4.3 contexts

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