source: opengl-game/new-game.cpp@ 0e0f851

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

Add a dedicated shader for asteroids, add an OpenGL debug callback, and start implementing the ability for asteroids to change color as they take damage.

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