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

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

Create addObjectToScene function to automate some aspects of SceneObject creation

  • Property mode set to 100644
File size: 35.6 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#define GLM_SWIZZLE
11
12// This is to fix a non-alignment issue when passing vec4 params.
13// Check if it got fixed in a later version of GLM
14#define GLM_FORCE_PURE
15
16#include <glm/mat4x4.hpp>
17#include <glm/gtc/matrix_transform.hpp>
18#include <glm/gtc/type_ptr.hpp>
19
20#include "IMGUI/imgui.h"
21#include "imgui_impl_glfw_gl3.h"
22
23#include <GL/glew.h>
24#include <GLFW/glfw3.h>
25
26#include <cstdio>
27#include <iostream>
28#include <fstream>
29#include <cmath>
30#include <string>
31#include <array>
32#include <vector>
33#include <queue>
34
35using namespace std;
36using namespace glm;
37
38#define ONE_DEG_IN_RAD (2.0 * M_PI) / 360.0 // 0.017444444
39
40struct SceneObject {
41 unsigned int id;
42 mat4 model_mat;
43 GLuint shader_program;
44 unsigned int num_points;
45 GLint vertex_vbo_offset;
46 vector<GLfloat> points;
47 vector<GLfloat> colors;
48 vector<GLfloat> texcoords;
49 vector<GLfloat> normals;
50 vector<GLfloat> selected_colors;
51};
52
53enum State {
54 STATE_MAIN_MENU,
55 STATE_GAME,
56};
57
58enum Event {
59 EVENT_GO_TO_MAIN_MENU,
60 EVENT_GO_TO_GAME,
61 EVENT_QUIT,
62};
63
64const bool FULLSCREEN = false;
65const bool SHOW_FPS = false;
66const bool DISABLE_VSYNC = true;
67unsigned int MAX_UNIFORMS = 0; // Requires OpenGL constants only available at runtime
68
69int width = 640;
70int height = 480;
71
72double fps;
73
74vec3 cam_pos;
75
76mat4 view_mat;
77mat4 proj_mat;
78
79vector<SceneObject> objects;
80queue<Event> events;
81
82SceneObject* clickedObject = NULL;
83SceneObject* selectedObject;
84
85float NEAR_CLIP = 0.1f;
86float FAR_CLIP = 100.0f;
87
88// Should really have some array or struct of UI-related variables
89bool isRunning = true;
90
91ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
92
93void glfw_error_callback(int error, const char* description);
94
95void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
96
97bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point);
98bool insideTriangle(vec3 p, array<vec3, 3> triangle_points);
99
100GLuint loadShader(GLenum type, string file);
101GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath);
102unsigned char* loadImage(string file_name, int* x, int* y);
103
104void printVector(string label, vec3 v);
105void print4DVector(string label, vec4 v);
106
107void addObjectToScene(SceneObject& obj);
108
109void renderMainMenu();
110void renderMainMenuGui();
111
112void renderScene(vector<SceneObject>& objects,
113 GLuint color_sp, GLuint texture_sp,
114 GLuint vao1, GLuint vao2,
115 GLuint points_vbo, GLuint normals_vbo,
116 GLuint colors_vbo, GLuint texcoords_vbo, GLuint selected_colors_vbo,
117 SceneObject* selectedObject);
118void renderSceneGui();
119
120int main(int argc, char* argv[]) {
121 cout << "New OpenGL Game" << endl;
122
123 if (!restart_gl_log()) {}
124 gl_log("starting GLFW\n%s\n", glfwGetVersionString());
125
126 glfwSetErrorCallback(glfw_error_callback);
127 if (!glfwInit()) {
128 fprintf(stderr, "ERROR: could not start GLFW3\n");
129 return 1;
130 }
131
132#ifdef __APPLE__
133 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
134 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
135 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
136 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
137#endif
138
139 glfwWindowHint(GLFW_SAMPLES, 4);
140
141 GLFWwindow* window = NULL;
142 GLFWmonitor* mon = NULL;
143
144 if (FULLSCREEN) {
145 mon = glfwGetPrimaryMonitor();
146 const GLFWvidmode* vmode = glfwGetVideoMode(mon);
147
148 width = vmode->width;
149 height = vmode->height;
150 cout << "Fullscreen resolution " << vmode->width << "x" << vmode->height << endl;
151 }
152 window = glfwCreateWindow(width, height, "New OpenGL Game", mon, NULL);
153
154 if (!window) {
155 fprintf(stderr, "ERROR: could not open window with GLFW3\n");
156 glfwTerminate();
157 return 1;
158 }
159
160 glfwMakeContextCurrent(window);
161 glewExperimental = GL_TRUE;
162 glewInit();
163
164 /*
165 * RENDERING ALGORITHM NOTES:
166 *
167 * Basically, I need to split my objects into groups, so that each group fits into
168 * GL_MAX_UNIFORM_BLOCK_SIZE. I need to have an offset and a size for each group.
169 * Getting the offset is straitforward. The size may as well be GL_MAX_UNIFORM_BLOCK_SIZE
170 * for each group, since it seems that smaller sizes just round up to the nearest GL_MAX_UNIFORM_BLOCK_SIZE
171 *
172 * I'll need to have a loop inside my render loop that calls glBindBufferRange(GL_UNIFORM_BUFFER, ...
173 * for every 1024 objects and then draws all those objects with one glDraw call.
174 *
175 * Since I'm currently drawing all my objects dynamically (i.e switcing the shaders they use),
176 * I'll table the implementation of this algorithm until I have a reasonable number of objects always using the same shader
177 */
178
179 GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT, MAX_UNIFORM_BLOCK_SIZE;
180 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &UNIFORM_BUFFER_OFFSET_ALIGNMENT);
181 glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &MAX_UNIFORM_BLOCK_SIZE);
182
183 MAX_UNIFORMS = MAX_UNIFORM_BLOCK_SIZE / sizeof(mat4);
184
185 cout << "UNIFORM_BUFFER_OFFSET_ALIGNMENT: " << UNIFORM_BUFFER_OFFSET_ALIGNMENT << endl;
186 cout << "MAX_UNIFORMS: " << MAX_UNIFORMS << endl;
187
188 // Setup Dear ImGui binding
189 IMGUI_CHECKVERSION();
190 ImGui::CreateContext();
191 ImGuiIO& io = ImGui::GetIO(); (void)io;
192 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
193 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
194 ImGui_ImplGlfwGL3_Init(window, true);
195
196 // Setup style
197 ImGui::StyleColorsDark();
198 //ImGui::StyleColorsClassic();
199
200 glfwSetMouseButtonCallback(window, mouse_button_callback);
201
202 const GLubyte* renderer = glGetString(GL_RENDERER);
203 const GLubyte* version = glGetString(GL_VERSION);
204 printf("Renderer: %s\n", renderer);
205 printf("OpenGL version supported %s\n", version);
206
207 glEnable(GL_DEPTH_TEST);
208 glDepthFunc(GL_LESS);
209
210 glEnable(GL_CULL_FACE);
211 // glCullFace(GL_BACK);
212 // glFrontFace(GL_CW);
213
214 int x, y;
215 unsigned char* texImage = loadImage("test.png", &x, &y);
216 if (texImage) {
217 cout << "Yay, I loaded an image!" << endl;
218 cout << x << endl;
219 cout << y << endl;
220 printf("first 4 bytes are: %i %i %i %i\n", texImage[0], texImage[1], texImage[2], texImage[3]);
221 }
222
223 GLuint tex = 0;
224 glGenTextures(1, &tex);
225 glActiveTexture(GL_TEXTURE0);
226 glBindTexture(GL_TEXTURE_2D, tex);
227 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
228
229 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
230 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
231 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
232 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
233
234 // I can create a vbo to store all points for all models,
235 // and another vbo to store all colors for all models, but how do I allow alternating between
236 // using colors and textures for each model?
237 // Do I create a third vbo for texture coordinates and change which vertex attribute array I have bound
238 // when I want to draw a textured model?
239 // Or do I create one vao with vertices and colors and another with vertices and textures and switch between the two?
240 // Since I would have to switch shader programs to toggle between using colors or textures,
241 // I think I should use one vao for both cases and have a points vbo, a colors vbo, and a textures vbo
242 // One program will use the points and colors, and the other will use the points and texture coords
243 // Review how to bind vbos to vertex attributes in the shader.
244 //
245 // Binding vbos is done using glVertexAttribPointer(...) on a per-vao basis and is not tied to any specific shader.
246 // This means, I could create two vaos, one for each shader and have one use points+colors, while the other
247 // uses points+texxcoords.
248 //
249 // At some point, when I have lots of objects, I want to group them by shader when drawing them.
250 // I'd probably create some sort of set per shader and have each set contain the ids of all objects currently using that shader
251 // Most likely, I'd want to implement each set using a bit field. Makes it constant time for updates and iterating through them
252 // should not be much of an issue either.
253 // Assuming making lots of draw calls instead of one is not innefficient, I should be fine.
254 // I might also want to use one glDrawElements call per shader to draw multiple non-memory-adjacent models
255 //
256 // DECISION: Use a glDrawElements call per shader since I use a regular array to specify the elements to draw
257 // Actually, this will only work once I get UBOs working since each object will have a different model matrix
258 // For now, I could implement this with a glDrawElements call per object and update the model uniform for each object
259
260 GLuint color_sp = loadShaderProgram("./color.vert", "./color.frag");
261 GLuint texture_sp = loadShaderProgram("./texture.vert", "./texture.frag");
262
263 SceneObject obj;
264 mat4 T_model, R_model;
265
266 // triangle
267 obj = SceneObject();
268 obj.shader_program = color_sp;
269 obj.points = {
270 0.0f, 0.5f, 0.0f,
271 -0.5f, -0.5f, 0.0f,
272 0.5f, -0.5f, 0.0f,
273 0.5f, -0.5f, 0.0f,
274 -0.5f, -0.5f, 0.0f,
275 0.0f, 0.5f, 0.0f,
276 };
277 obj.colors = {
278 1.0f, 0.0f, 0.0f,
279 0.0f, 0.0f, 1.0f,
280 0.0f, 1.0f, 0.0f,
281 0.0f, 1.0f, 0.0f,
282 0.0f, 0.0f, 1.0f,
283 1.0f, 0.0f, 0.0f,
284 };
285 obj.texcoords = {
286 1.0f, 1.0f,
287 0.0f, 1.0f,
288 0.0f, 0.0f,
289 1.0f, 1.0f,
290 0.0f, 0.0f,
291 1.0f, 0.0f
292 };
293 obj.selected_colors = {
294 0.0f, 1.0f, 0.0f,
295 0.0f, 1.0f, 0.0f,
296 0.0f, 1.0f, 0.0f,
297 0.0f, 1.0f, 0.0f,
298 0.0f, 1.0f, 0.0f,
299 0.0f, 1.0f, 0.0f,
300 };
301
302 T_model = translate(mat4(), vec3(0.45f, 0.0f, 0.0f));
303 R_model = rotate(mat4(), 0.0f, vec3(0.0f, 1.0f, 0.0f));
304 obj.model_mat = T_model*R_model;
305
306 addObjectToScene(obj);
307
308 // square
309 obj = SceneObject();
310 obj.shader_program = texture_sp;
311 obj.points = {
312 0.5f, 0.5f, 0.0f,
313 -0.5f, 0.5f, 0.0f,
314 -0.5f, -0.5f, 0.0f,
315 0.5f, 0.5f, 0.0f,
316 -0.5f, -0.5f, 0.0f,
317 0.5f, -0.5f, 0.0f,
318 };
319 obj.colors = {
320 1.0f, 0.0f, 0.0f,
321 0.0f, 0.0f, 1.0f,
322 0.0f, 1.0f, 0.0f,
323 0.0f, 1.0f, 0.0f,
324 0.0f, 0.0f, 1.0f,
325 1.0f, 0.0f, 0.0f,
326 };
327 obj.texcoords = {
328 1.0f, 1.0f,
329 0.0f, 1.0f,
330 0.0f, 0.0f,
331 1.0f, 1.0f,
332 0.0f, 0.0f,
333 1.0f, 0.0f
334 };
335 obj.selected_colors = {
336 0.0f, 0.6f, 0.9f,
337 0.0f, 0.6f, 0.9f,
338 0.0f, 0.6f, 0.9f,
339 0.0f, 0.6f, 0.9f,
340 0.0f, 0.6f, 0.9f,
341 0.0f, 0.6f, 0.9f,
342 };
343
344 T_model = translate(mat4(), vec3(-0.5f, 0.0f, -1.00f));
345 R_model = rotate(mat4(), 0.5f, vec3(0.0f, 1.0f, 0.0f));
346 obj.model_mat = T_model*R_model;
347
348 addObjectToScene(obj);
349
350 // player ship
351 obj = SceneObject();
352 obj.shader_program = color_sp;
353 obj.points = {
354 0.0f, 0.5f, 0.0f,
355 -0.5f, -0.5f, 0.0f,
356 0.5f, -0.5f, 0.0f,
357 0.5f, -0.5f, 0.0f,
358 -0.5f, -0.5f, 0.0f,
359 0.0f, 0.5f, 0.0f,
360 };
361 obj.colors = {
362 0.0f, 0.0f, 0.3f,
363 0.0f, 0.0f, 0.3f,
364 0.0f, 0.0f, 0.3f,
365 0.0f, 0.0f, 0.3f,
366 0.0f, 0.0f, 0.3f,
367 0.0f, 0.0f, 0.3f,
368 };
369 obj.texcoords = {
370 1.0f, 1.0f,
371 0.0f, 1.0f,
372 0.0f, 0.0f,
373 1.0f, 1.0f,
374 0.0f, 0.0f,
375 1.0f, 0.0f
376 };
377 obj.selected_colors = {
378 0.0f, 1.0f, 0.0f,
379 0.0f, 1.0f, 0.0f,
380 0.0f, 1.0f, 0.0f,
381 0.0f, 1.0f, 0.0f,
382 0.0f, 1.0f, 0.0f,
383 0.0f, 1.0f, 0.0f,
384 };
385
386 T_model = translate(mat4(), vec3(0.0f, -0.65f, 0.0f));
387 R_model = rotate(mat4(), -1.0f, vec3(1.0f, 0.0f, 0.0f));
388 obj.model_mat = T_model * R_model;
389
390 addObjectToScene(obj);
391
392 vector<SceneObject>::iterator obj_it;
393 GLsizeiptr offset;
394
395 GLsizeiptr points_buffer_size = 0;
396 GLsizeiptr textures_buffer_size = 0;
397 GLsizeiptr ubo_buffer_size = 0;
398 GLsizeiptr model_mat_idx_buffer_size = 0;
399
400 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
401 points_buffer_size += obj_it->points.size() * sizeof(GLfloat);
402 textures_buffer_size += obj_it->texcoords.size() * sizeof(GLfloat);
403 ubo_buffer_size += 16 * sizeof(GLfloat);
404 model_mat_idx_buffer_size += obj_it->num_points * sizeof(GLuint);
405 }
406
407 GLuint points_vbo = 0;
408 glGenBuffers(1, &points_vbo);
409 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
410 glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
411
412 offset = 0;
413 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
414 glBufferSubData(GL_ARRAY_BUFFER, offset, obj_it->points.size() * sizeof(GLfloat), &obj_it->points[0]);
415 offset += obj_it->points.size() * sizeof(GLfloat);
416 }
417
418 GLuint colors_vbo = 0;
419 glGenBuffers(1, &colors_vbo);
420 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
421 glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
422
423 offset = 0;
424 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
425 glBufferSubData(GL_ARRAY_BUFFER, offset, obj_it->colors.size() * sizeof(GLfloat), &obj_it->colors[0]);
426 offset += obj_it->colors.size() * sizeof(GLfloat);
427 }
428
429 GLuint selected_colors_vbo = 0;
430 glGenBuffers(1, &selected_colors_vbo);
431 glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
432 glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
433
434 offset = 0;
435 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
436 glBufferSubData(GL_ARRAY_BUFFER, offset, obj_it->selected_colors.size() * sizeof(GLfloat), &obj_it->selected_colors[0]);
437 offset += obj_it->selected_colors.size() * sizeof(GLfloat);
438 }
439
440 GLuint texcoords_vbo = 0;
441 glGenBuffers(1, &texcoords_vbo);
442 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
443 glBufferData(GL_ARRAY_BUFFER, textures_buffer_size, NULL, GL_DYNAMIC_DRAW);
444
445 offset = 0;
446 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
447 glBufferSubData(GL_ARRAY_BUFFER, offset, obj_it->texcoords.size() * sizeof(GLfloat), &obj_it->texcoords[0]);
448 offset += obj_it->texcoords.size() * sizeof(GLfloat);
449 }
450
451 GLuint normals_vbo = 0;
452 glGenBuffers(1, &normals_vbo);
453 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
454 glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
455
456 offset = 0;
457 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
458 glBufferSubData(GL_ARRAY_BUFFER, offset, obj_it->normals.size() * sizeof(GLfloat), &obj_it->normals[0]);
459 offset += obj_it->normals.size() * sizeof(GLfloat);
460 }
461 glBindBuffer(GL_UNIFORM_BUFFER, 0);
462
463 GLuint ubo = 0;
464 glGenBuffers(1, &ubo);
465 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
466 glBufferData(GL_UNIFORM_BUFFER, ubo_buffer_size, NULL, GL_DYNAMIC_DRAW);
467
468 offset = 0;
469 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
470 glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(obj_it->model_mat), value_ptr(obj_it->model_mat));
471 offset += sizeof(obj_it->model_mat);
472 }
473 glBindBuffer(GL_UNIFORM_BUFFER, 0);
474
475 GLuint model_mat_idx_vbo = 0;
476 glGenBuffers(1, &model_mat_idx_vbo);
477 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
478 glBufferData(GL_ARRAY_BUFFER, model_mat_idx_buffer_size, NULL, GL_DYNAMIC_DRAW);
479
480 offset = 0;
481 unsigned int idx = 0;
482 for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
483 for (int i = 0; i < obj_it->num_points; i++) {
484 glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLuint), &idx);
485 offset += sizeof(GLuint);
486 }
487 idx++;
488 }
489
490 GLuint vao = 0;
491 glGenVertexArrays(1, &vao);
492 glBindVertexArray(vao);
493
494 glEnableVertexAttribArray(0);
495 glEnableVertexAttribArray(1);
496 glEnableVertexAttribArray(2);
497 glEnableVertexAttribArray(3);
498
499 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
500 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
501
502 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
503 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
504
505 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
506 glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, 0);
507
508 GLuint vao2 = 0;
509 glGenVertexArrays(1, &vao2);
510 glBindVertexArray(vao2);
511
512 glEnableVertexAttribArray(0);
513 glEnableVertexAttribArray(1);
514 glEnableVertexAttribArray(2);
515 glEnableVertexAttribArray(3);
516
517 glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
518 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
519
520 glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
521 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
522
523 glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
524 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
525
526 glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
527 glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, 0);
528
529 float speed = 1.0f;
530 float last_position = 0.0f;
531
532 float cam_speed = 1.0f;
533 float cam_yaw_speed = 60.0f*ONE_DEG_IN_RAD;
534
535 // glm::lookAt can create the view matrix
536 // glm::perspective can create the projection matrix
537
538 cam_pos = vec3(0.0f, 0.0f, 2.0f);
539 float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
540
541 mat4 T = translate(mat4(), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
542 mat4 R = rotate(mat4(), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
543 view_mat = R*T;
544
545 float fov = 67.0f * ONE_DEG_IN_RAD;
546 float aspect = (float)width / (float)height;
547
548 float range = tan(fov * 0.5f) * NEAR_CLIP;
549 float Sx = NEAR_CLIP / (range * aspect);
550 float Sy = NEAR_CLIP / range;
551 float Sz = -(FAR_CLIP + NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
552 float Pz = -(2.0f * FAR_CLIP * NEAR_CLIP) / (FAR_CLIP - NEAR_CLIP);
553
554 float proj_arr[] = {
555 Sx, 0.0f, 0.0f, 0.0f,
556 0.0f, Sy, 0.0f, 0.0f,
557 0.0f, 0.0f, Sz, -1.0f,
558 0.0f, 0.0f, Pz, 0.0f,
559 };
560 proj_mat = make_mat4(proj_arr);
561
562 GLuint ub_binding_point = 0;
563
564 GLuint view_test_loc = glGetUniformLocation(color_sp, "view");
565 GLuint proj_test_loc = glGetUniformLocation(color_sp, "proj");
566 GLuint color_sp_ub_index = glGetUniformBlockIndex(color_sp, "models");
567
568 GLuint view_mat_loc = glGetUniformLocation(texture_sp, "view");
569 GLuint proj_mat_loc = glGetUniformLocation(texture_sp, "proj");
570 GLuint texture_sp_ub_index = glGetUniformBlockIndex(texture_sp, "models");
571
572 glUseProgram(color_sp);
573 glUniformMatrix4fv(view_test_loc, 1, GL_FALSE, value_ptr(view_mat));
574 glUniformMatrix4fv(proj_test_loc, 1, GL_FALSE, value_ptr(proj_mat));
575
576 glUniformBlockBinding(color_sp, color_sp_ub_index, ub_binding_point);
577 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
578
579 glUseProgram(texture_sp);
580 glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
581 glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
582
583 glUniformBlockBinding(texture_sp, texture_sp_ub_index, ub_binding_point);
584 glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
585
586 bool cam_moved = false;
587
588 int frame_count = 0;
589 double elapsed_seconds_fps = 0.0f;
590 double previous_seconds = glfwGetTime();
591
592 // This draws wireframes. Useful for seeing separate faces and occluded objects.
593 //glPolygonMode(GL_FRONT, GL_LINE);
594
595 // disable vsync to see real framerate
596 if (DISABLE_VSYNC && SHOW_FPS) {
597 glfwSwapInterval(0);
598 }
599
600 State curState = STATE_MAIN_MENU;
601
602 while (!glfwWindowShouldClose(window) && isRunning) {
603 double current_seconds = glfwGetTime();
604 double elapsed_seconds = current_seconds - previous_seconds;
605 previous_seconds = current_seconds;
606
607 if (SHOW_FPS) {
608 elapsed_seconds_fps += elapsed_seconds;
609 if (elapsed_seconds_fps > 0.25f) {
610 fps = (double)frame_count / elapsed_seconds_fps;
611 cout << "FPS: " << fps << endl;
612
613 frame_count = 0;
614 elapsed_seconds_fps = 0.0f;
615 }
616
617 frame_count++;
618 }
619
620 if (fabs(last_position) > 1.0f) {
621 speed = -speed;
622 }
623
624 // Handle events (Ideally, move all event-handling code
625 // before the render code)
626
627 clickedObject = NULL;
628 glfwPollEvents();
629
630 while (!events.empty()) {
631 switch (events.front()) {
632 case EVENT_GO_TO_MAIN_MENU:
633 curState = STATE_MAIN_MENU;
634 break;
635 case EVENT_GO_TO_GAME:
636 curState = STATE_GAME;
637 break;
638 case EVENT_QUIT:
639 isRunning = false;
640 break;
641 }
642 events.pop();
643 }
644
645 if (curState == STATE_GAME) {
646 if (clickedObject == &objects[0]) {
647 selectedObject = &objects[0];
648 }
649 if (clickedObject == &objects[1]) {
650 selectedObject = &objects[1];
651 }
652 }
653
654 /*
655 model[12] = last_position + speed*elapsed_seconds;
656 last_position = model[12];
657 */
658
659 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
660
661 switch (curState) {
662 case STATE_MAIN_MENU:
663 renderMainMenu();
664 renderMainMenuGui();
665 break;
666 case STATE_GAME:
667 renderScene(objects,
668 color_sp, texture_sp,
669 vao, vao2,
670 points_vbo, normals_vbo,
671 colors_vbo, texcoords_vbo, selected_colors_vbo,
672 selectedObject);
673 renderSceneGui();
674 break;
675 }
676
677 glfwSwapBuffers(window);
678
679 if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE)) {
680 glfwSetWindowShouldClose(window, 1);
681 }
682
683 float dist = cam_speed * elapsed_seconds;
684 if (glfwGetKey(window, GLFW_KEY_A)) {
685 cam_pos.x -= cos(cam_yaw)*dist;
686 cam_pos.z += sin(cam_yaw)*dist;
687 cam_moved = true;
688 }
689 if (glfwGetKey(window, GLFW_KEY_D)) {
690 cam_pos.x += cos(cam_yaw)*dist;
691 cam_pos.z -= sin(cam_yaw)*dist;
692 cam_moved = true;
693 }
694 if (glfwGetKey(window, GLFW_KEY_W)) {
695 cam_pos.x -= sin(cam_yaw)*dist;
696 cam_pos.z -= cos(cam_yaw)*dist;
697 cam_moved = true;
698 }
699 if (glfwGetKey(window, GLFW_KEY_S)) {
700 cam_pos.x += sin(cam_yaw)*dist;
701 cam_pos.z += cos(cam_yaw)*dist;
702 cam_moved = true;
703 }
704 if (glfwGetKey(window, GLFW_KEY_LEFT)) {
705 cam_yaw += cam_yaw_speed * elapsed_seconds;
706 cam_moved = true;
707 }
708 if (glfwGetKey(window, GLFW_KEY_RIGHT)) {
709 cam_yaw -= cam_yaw_speed * elapsed_seconds;
710 cam_moved = true;
711 }
712 if (cam_moved) {
713 T = translate(mat4(), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
714 R = rotate(mat4(), -cam_yaw, vec3(0.0f, 1.0f, 0.0f));
715 view_mat = R*T;
716
717 glUseProgram(color_sp);
718 glUniformMatrix4fv(view_test_loc, 1, GL_FALSE, value_ptr(view_mat));
719
720 glUseProgram(texture_sp);
721 glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
722
723 cam_moved = false;
724 }
725 }
726
727 ImGui_ImplGlfwGL3_Shutdown();
728 ImGui::DestroyContext();
729
730 glfwDestroyWindow(window);
731 glfwTerminate();
732
733 return 0;
734}
735
736void glfw_error_callback(int error, const char* description) {
737 gl_log_err("GLFW ERROR: code %i msg: %s\n", error, description);
738}
739
740void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
741 double mouse_x, mouse_y;
742 glfwGetCursorPos(window, &mouse_x, &mouse_y);
743
744 if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
745 cout << "Mouse clicked (" << mouse_x << "," << mouse_y << ")" << endl;
746 selectedObject = NULL;
747
748 float x = (2.0f*mouse_x) / width - 1.0f;
749 float y = 1.0f - (2.0f*mouse_y) / height;
750
751 cout << "x: " << x << ", y: " << y << endl;
752
753 vec4 ray_clip = vec4(x, y, -1.0f, 1.0f);
754 vec4 ray_eye = inverse(proj_mat) * ray_clip;
755 ray_eye = vec4(ray_eye.xy(), -1.0f, 1.0f);
756 vec4 ray_world = inverse(view_mat) * ray_eye;
757
758 vec4 cam_pos_temp = vec4(cam_pos, 1.0f);
759
760 vec4 click_point;
761 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
762 SceneObject* closest_object = NULL;
763
764 SceneObject* obj;
765 for (vector<SceneObject>::iterator it = objects.begin(); it != objects.end(); it++) {
766 obj = &*it;
767
768 for (unsigned int p_idx = 0; p_idx < it->points.size(); p_idx += 9) {
769 if (faceClicked({
770 vec3(it->points[p_idx], it->points[p_idx + 1], it->points[p_idx + 2]),
771 vec3(it->points[p_idx + 3], it->points[p_idx + 4], it->points[p_idx + 5]),
772 vec3(it->points[p_idx + 6], it->points[p_idx + 7], it->points[p_idx + 8]),
773 },
774 obj, ray_world, cam_pos_temp, click_point)) {
775 click_point = view_mat * click_point;
776
777 if (-NEAR_CLIP >= click_point.z && click_point.z > -FAR_CLIP && click_point.z > closest_point.z) {
778 closest_point = click_point.xyz();
779 closest_object = obj;
780 }
781 }
782 }
783 }
784
785 if (closest_object == NULL) {
786 cout << "No object was clicked" << endl;
787 }
788 else {
789 clickedObject = closest_object;
790 cout << "Clicked object: " << clickedObject->id << endl;
791 }
792 }
793}
794
795GLuint loadShader(GLenum type, string file) {
796 cout << "Loading shader from file " << file << endl;
797
798 ifstream shaderFile(file);
799 GLuint shaderId = 0;
800
801 if (shaderFile.is_open()) {
802 string line, shaderString;
803
804 while(getline(shaderFile, line)) {
805 shaderString += line + "\n";
806 }
807 shaderFile.close();
808 const char* shaderCString = shaderString.c_str();
809
810 shaderId = glCreateShader(type);
811 glShaderSource(shaderId, 1, &shaderCString, NULL);
812 glCompileShader(shaderId);
813
814 cout << "Loaded successfully" << endl;
815 } else {
816 cout << "Failed to load the file" << endl;
817 }
818
819 return shaderId;
820}
821
822GLuint loadShaderProgram(string vertexShaderPath, string fragmentShaderPath) {
823 GLuint vs = loadShader(GL_VERTEX_SHADER, vertexShaderPath);
824 GLuint fs = loadShader(GL_FRAGMENT_SHADER, fragmentShaderPath);
825
826 GLuint shader_program = glCreateProgram();
827 glAttachShader(shader_program, vs);
828 glAttachShader(shader_program, fs);
829
830 glLinkProgram(shader_program);
831
832 return shader_program;
833}
834
835unsigned char* loadImage(string file_name, int* x, int* y) {
836 int n;
837 int force_channels = 4; // This forces RGBA (4 bytes per pixel)
838 unsigned char* image_data = stbi_load(file_name.c_str(), x, y, &n, force_channels);
839
840 int width_in_bytes = *x * 4;
841 unsigned char *top = NULL;
842 unsigned char *bottom = NULL;
843 unsigned char temp = 0;
844 int half_height = *y / 2;
845
846 // flip image upside-down to account for OpenGL treating lower-left as (0, 0)
847 for (int row = 0; row < half_height; row++) {
848 top = image_data + row * width_in_bytes;
849 bottom = image_data + (*y - row - 1) * width_in_bytes;
850 for (int col = 0; col < width_in_bytes; col++) {
851 temp = *top;
852 *top = *bottom;
853 *bottom = temp;
854 top++;
855 bottom++;
856 }
857 }
858
859 if (!image_data) {
860 fprintf(stderr, "ERROR: could not load %s\n", file_name.c_str());
861 }
862
863 // Not Power-of-2 check
864 if ((*x & (*x - 1)) != 0 || (*y & (*y - 1)) != 0) {
865 fprintf(stderr, "WARNING: texture %s is not power-of-2 dimensions\n", file_name.c_str());
866 }
867
868 return image_data;
869}
870
871bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point) {
872 // LINE EQUATION: P = O + Dt
873 // O = cam
874 // D = ray_world
875
876 // PLANE EQUATION: P dot n + d = 0
877 // n is the normal vector
878 // d is the offset from the origin
879
880 // Take the cross-product of two vectors on the plane to get the normal
881 vec3 v1 = points[1] - points[0];
882 vec3 v2 = points[2] - points[0];
883
884 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);
885
886 print4DVector("Full world ray", world_ray);
887
888 vec3 local_ray = (inverse(obj->model_mat) * world_ray).xyz();
889 vec3 local_cam = (inverse(obj->model_mat) * cam).xyz();
890
891 local_ray = local_ray - local_cam;
892
893 float d = -glm::dot(points[0], normal);
894 cout << "d: " << d << endl;
895
896 float t = -(glm::dot(local_cam, normal) + d) / glm::dot(local_ray, normal);
897 cout << "t: " << t << endl;
898
899 vec3 intersection = local_cam + t*local_ray;
900 printVector("Intersection", intersection);
901
902 if (insideTriangle(intersection, points)) {
903 click_point = obj->model_mat * vec4(intersection, 1.0f);
904 return true;
905 } else {
906 return false;
907 }
908}
909bool insideTriangle(vec3 p, array<vec3, 3> triangle_points) {
910 vec3 v21 = triangle_points[1] - triangle_points[0];
911 vec3 v31 = triangle_points[2] - triangle_points[0];
912 vec3 pv1 = p - triangle_points[0];
913
914 float y = (pv1.y*v21.x - pv1.x*v21.y) / (v31.y*v21.x - v31.x*v21.y);
915 float x = (pv1.x-y*v31.x) / v21.x;
916
917 return x > 0.0f && y > 0.0f && x+y < 1.0f;
918}
919
920void printVector(string label, vec3 v) {
921 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << ")" << endl;
922}
923
924void print4DVector(string label, vec4 v) {
925 cout << label << " -> (" << v.x << "," << v.y << "," << v.z << "," << v.w << ")" << endl;
926}
927
928// The easiest thing here seems to be to set all the colors we want in a CPU array once per frame,
929// them copy it over to the GPU and make one draw call
930// This method also easily allows us to use any colors we want for each shape.
931// Try to compare the frame times for the current method and the new one
932//
933// Alternatively, I have one large color buffer that has selected and unselected colors
934// Then, when I know which object is selected, I can use glVertexAttribPointer to decide
935// whether to use the selected or unselected color for it
936
937// I'll have to think of the best way to do something similar when using
938// one draw call. Probably, in that case, I'll use one draw call for all unselectable objects
939// and use the approach mentioned above for all selectable objects.
940// I can have one colors vbo for unselectable objects and another for selected+unselected colors
941// of selectable objects
942
943// For both colored and textured objects, using a single draw call will only work for objects
944// that don't change state (i.e. don't change colors or switch from colored to textured).
945
946// This means I'll probably have one call for static colored objects, one call for static textured objects,
947// a loop of calls for dynamic currently colored objects, and a loop of calls for dynamic currently textured objects.
948// This will increase if I add new shaders since I'll need either one new call or one new loop of calls per shader
949
950void renderScene(vector<SceneObject>& objects,
951 GLuint color_sp, GLuint texture_sp,
952 GLuint vao1, GLuint vao2,
953 GLuint points_vbo, GLuint normals_vbo,
954 GLuint colors_vbo, GLuint texcoords_vbo, GLuint selected_colors_vbo,
955 SceneObject* selectedObject) {
956
957 vector<int> colored_objs, selected_objs, textured_objs, static_colored_objs, static_textured_objs;
958
959 // group scene objects by shader and vbo
960 for (unsigned int i = 0; i < objects.size(); i++) {
961 if (selectedObject == &objects[i]) {
962 selected_objs.push_back(i);
963 } else if (objects[i].shader_program == color_sp) {
964 colored_objs.push_back(i);
965 } else if (objects[i].shader_program == texture_sp) {
966 textured_objs.push_back(i);
967 }
968 }
969
970 vector<int>::iterator it;
971
972 glUseProgram(color_sp);
973 glBindVertexArray(vao1);
974
975 glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
976 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
977
978 for (it = colored_objs.begin(); it != colored_objs.end(); it++) {
979 glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
980 }
981
982 glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
983 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
984
985 for (it = selected_objs.begin(); it != selected_objs.end(); it++) {
986 glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
987 }
988
989 glUseProgram(texture_sp);
990 glBindVertexArray(vao2);
991
992 for (it = textured_objs.begin(); it != textured_objs.end(); it++) {
993 glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
994 }
995}
996
997void renderSceneGui() {
998 ImGui_ImplGlfwGL3_NewFrame();
999
1000 // 1. Show a simple window.
1001 // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug".
1002 /*
1003 {
1004 static float f = 0.0f;
1005 static int counter = 0;
1006 ImGui::Text("Hello, world!"); // Display some text (you can use a format string too)
1007 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
1008 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
1009
1010 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state
1011 ImGui::Checkbox("Another Window", &show_another_window);
1012
1013 if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated)
1014 counter++;
1015 ImGui::SameLine();
1016 ImGui::Text("counter = %d", counter);
1017
1018 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
1019 }
1020 */
1021
1022 {
1023 ImGui::SetNextWindowSize(ImVec2(85, 22), ImGuiCond_Once);
1024 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
1025 ImGui::Begin("WndStats", NULL,
1026 ImGuiWindowFlags_NoTitleBar |
1027 ImGuiWindowFlags_NoResize |
1028 ImGuiWindowFlags_NoMove);
1029 ImGui::Text("Score: ???");
1030 ImGui::End();
1031 }
1032
1033 {
1034 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
1035 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
1036 ImGui::Begin("WndMenubar", NULL,
1037 ImGuiWindowFlags_NoTitleBar |
1038 ImGuiWindowFlags_NoResize |
1039 ImGuiWindowFlags_NoMove);
1040 ImGui::InvisibleButton("", ImVec2(155, 18));
1041 ImGui::SameLine();
1042 if (ImGui::Button("Main Menu")) {
1043 events.push(EVENT_GO_TO_MAIN_MENU);
1044 }
1045 ImGui::End();
1046 }
1047
1048 ImGui::Render();
1049 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
1050}
1051
1052void addObjectToScene(SceneObject& obj) {
1053 obj.id = objects.size(); // currently unused
1054 obj.num_points = obj.points.size() / 3;
1055
1056 static GLint vbo_offset = 0;
1057 obj.vertex_vbo_offset = vbo_offset;
1058 vbo_offset += obj.num_points;
1059
1060 obj.normals.reserve(obj.points.size());
1061 for (int i = 0; i < obj.points.size(); i += 9) {
1062 vec3 point1 = vec3(obj.points[i], obj.points[i+1], obj.points[i + 2]);
1063 vec3 point2 = vec3(obj.points[i+3], obj.points[i + 4], obj.points[i + 5]);
1064 vec3 point3 = vec3(obj.points[i+6], obj.points[i + 7], obj.points[i + 8]);
1065
1066 vec3 normal = normalize(cross(point2 - point1, point3 - point1));
1067
1068 // Add the same normal for all 3 points
1069 for (int j = 0; j < 3; j++) {
1070 obj.normals.push_back(normal.x);
1071 obj.normals.push_back(normal.y);
1072 obj.normals.push_back(normal.z);
1073 }
1074 }
1075
1076 objects.push_back(obj);
1077}
1078
1079void renderMainMenu() {
1080}
1081
1082void renderMainMenuGui() {
1083 ImGui_ImplGlfwGL3_NewFrame();
1084
1085 {
1086 int padding = 4;
1087 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
1088 ImGui::SetNextWindowSize(ImVec2(width + 2 * padding, height + 2 * padding), ImGuiCond_Once);
1089 ImGui::Begin("WndMain", NULL,
1090 ImGuiWindowFlags_NoTitleBar |
1091 ImGuiWindowFlags_NoResize |
1092 ImGuiWindowFlags_NoMove);
1093
1094 ImGui::InvisibleButton("", ImVec2(10, 80));
1095 ImGui::InvisibleButton("", ImVec2(285, 18));
1096 ImGui::SameLine();
1097 if (ImGui::Button("New Game")) {
1098 events.push(EVENT_GO_TO_GAME);
1099 }
1100
1101 ImGui::InvisibleButton("", ImVec2(10, 15));
1102 ImGui::InvisibleButton("", ImVec2(300, 18));
1103 ImGui::SameLine();
1104 if (ImGui::Button("Quit")) {
1105 events.push(EVENT_QUIT);
1106 }
1107
1108 ImGui::End();
1109 }
1110
1111 ImGui::Render();
1112 ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
1113}
Note: See TracBrowser for help on using the repository browser.