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

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

Update new-game.cpp to use a header-only version of stb_image

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