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

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

Fix the buffer resizing algorithm for model groups (this fixes the laser rendering issue)

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