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

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

Add a callback for the window resize event

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