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

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

For TYPE_SHIP objects, switch to using vbos specific to the model group as opposed to the generic vbos.

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