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

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

Create an AttribInfo array for each ShaderModelGroup to support generic handling of shader attributes

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