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

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

Update logging code to send all errors to the log file as well as the console

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