source: opengl-game/new-game.cpp@ 7a55b49

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

Create the ShaderModelGroup struct and start moving info required for rendering explosions into an instance of it

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