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

feature/imgui-sdl points-test
Last change on this file since caa2359 was caa2359, checked in by Dmitry Portnoy <dmp1488@…>, 5 years ago

Fix some logging code errors

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