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

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

Undo tweaks to badFunc

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