source: opengl-game/new-game.cpp@ 1fcca9e

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

Rename logger.h to logger.hpp

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