source: opengl-game/new-game.cpp@ 98f06d9

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

Add support for ofstream to logger.cpp

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