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

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

Remove all the generic vbos

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