source: opengl-game/new-game.cpp@ 49db5fc

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

Add support for uniform attributes in a ShaderModelGroup

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