source: opengl-game/new-game.cpp@ 7e10667

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

Finish switching to the new model group rendering algorithm for TYPE_EXPLOSION objects

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