source: opengl-game/new-game.cpp@ 6abfd07

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

Create a log with a stack-trace when the game crashes

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