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

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

Fix some logging code errors

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