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

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

Remove all the generic vbos

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