source: opengl-game/new-game.cpp@ 98f06d9

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

Add support for ofstream to logger.cpp

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