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

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

Make explosion objects use the global model mat uniform buffer and a varying ubo index attribute to access it

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