source: opengl-game/new-game.cpp@ 155a7cf

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

Replace printf with cout and cerr in new-game.cpp and logger.cpp

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