source: opengl-game/new-game.cpp@ 3de31cf

feature/imgui-sdl points-test
Last change on this file since 3de31cf was 4d84c72, checked in by Dmitry Portnoy <dmp1488@…>, 6 years ago

Move the OpenGL shaders to a gl-shaders folder

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