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

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

Update TODO and remove some unused files

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