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

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

CHange the code that copies object data into the OpenGL buffers into one generic loop and update the TODO list

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