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

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

Add a debug console to the game that displays program variable values

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