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

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

Move the generation of the explosion shader vbos to where all the other "global" vbos are generated

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