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

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

Change VulkanGame::addObject() to return a reference to the newly-created object

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