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

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

Mark which parts of the old code were moved over to the new openglgame and vulkangame code and remove some TODOs that were moved over

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