source: opengl-game/new-game.cpp@ 14e6918

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

Switch to the new model group rendering algorithm for TYPE_ASTEROID objects

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