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

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

Create an overloaded version of bindUniformData that takes the data as a parameter instead of relying on a previously set pointer

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