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

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

Initialize the explosion shader vertex attributes using the new generic shader system

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