source: opengl-game/new-game.cpp@ 5b02676

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

Make vulkangame and openglgame compile on OSX with the new GraphicsPipeline classes

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