-
Notifications
You must be signed in to change notification settings - Fork 0
/
fractal.c
347 lines (280 loc) · 10.2 KB
/
fractal.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
#include "fractal.h"
#include "shader.h"
#include "util.h"
#include "imaging.h"
#include "advinput.h"
// Stores resources for OpenGL in a global struct
static struct {
GLuint vbo; // Vertex Buffer Object
GLuint ibo; // Index Buffer Object
GLuint vao; // Vertex Array Object
GLuint frag_shader;
GLuint vert_shader;
GLuint prog_object;
GLuint color_palette;
struct {
GLint position;
} shader_attrib;
struct {
GLint zoom;
GLint offset;
GLint ratio;
GLint iter;
} shader_uniform;
} gl_data;
static double zoom = 1.0;
static double zoomAccel = 0.0;
const double zoomFactor = 0.01;
const double zoomInertia = 0.85;
static struct {
double scroll;
double coords[2];
double oldCoords[2];
} mouse;
static double offsetCoords[2] = {0.0, 0.0};
static struct {
int width;
int height;
float ratio;
GLFWwindow* handle;
} window;
static cliArgs config;
static int max_iterations = 100;
int main(int argc, char **argv) {
parseArgs(argc, argv, &config);
// Check whether the help command line option was specified and react accordingly
if(config.showHelp) {
outputHelpText();
return 0;
}
// default values for resolution
window.width = fallback(config.x_resolution, 800);
window.height = fallback(config.y_resolution, 600);
// set the starting offsets
offsetCoords[0] = fallback(config.startOffsetRe, 0.0);
offsetCoords[1] = fallback(config.startOffsetIm, 0.0);
zoom = fallback(config.startZoom, 1.0);
max_iterations = fallback(config.startIterations, 100);
config.showFPS = fallback(config.showFPS, 0);
if(initGraphics(window.width, window.height, config.useFullscreen, config.noVSync, config.numFSAASamples)) {
return 1; // if there has been an error, don't even bother to continue
}
mouse.scroll = 0;
window.ratio = (float)window.height / (float)window.width;
// Set the input callback function pointers for advinput
initAdvInput(window.handle);
return mainLoop(); // Call the main loop and return it's return code when quitting
};
int mainLoop() {
int isRunning = true;
int framesPassed = 0;
double frameDelta = 0;
glfwSetTime(0.0);
double frameTimer = glfwGetTime();
double lastFrame = glfwGetTime();
int iterChange = 0;
// Won't change this at runtime
glUseProgram(gl_data.prog_object);
// these uniforms remain constant or are only updated on certain events
glUniform1f(gl_data.shader_uniform.ratio, window.ratio);
glUniform1i(gl_data.shader_uniform.iter, max_iterations);
glUniform2d(gl_data.shader_uniform.offset, offsetCoords[0], offsetCoords[1]);
glVertexAttribPointer( gl_data.shader_attrib.position, // index
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized
sizeof(GLfloat)*2, // stride
NULL); // pointer
// no need to ever disable this, so we'll enable it here.
glEnableVertexAttribArray(gl_data.shader_attrib.position);
// Binding the buffers, they will stay bound through the entire course of the program.
glBindBuffer(GL_ARRAY_BUFFER, gl_data.vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_data.ibo);
while(isRunning) {
// FPS (Frames-Per-Second) measurement
if(glfwGetTime() - frameTimer >= 1.0) {
if(config.showFPS) {
printf("FPS: %lf\n", (double)framesPassed / (glfwGetTime() - frameTimer));
}
frameTimer = glfwGetTime();
framesPassed = 0;
}
frameDelta = (glfwGetTime() - lastFrame) / 0.01666666;
lastFrame = glfwGetTime();
// Update mouse coordinate variables
mouse.oldCoords[0] = mouse.coords[0]; mouse.oldCoords[1] = mouse.coords[1];
glfwGetCursorPos(window.handle, &mouse.coords[0], &mouse.coords[1]);
/*************************/
/*** Zoom calculations ***/
/*************************/
//fprintf(stderr, "%lf\n", max(zoomInertia + 0.15 - 0.15 * frameDelta, 0.25));
zoomAccel *= max(zoomInertia + 0.15 - 0.15 * frameDelta, 0.25);
// Scroll to zoom
zoomAccel -= mouse.scroll * zoomFactor * frameDelta;
// Double-Click to zoom
zoomAccel += (mouseDoubleClicked(GLFW_MOUSE_BUTTON_RIGHT) - mouseDoubleClicked(GLFW_MOUSE_BUTTON_LEFT)) * 0.1 * min(frameDelta, 9.9);
// This line may need explanation
// it cuts off the zoomAccel value if it's too small to make a noticeable change within one frame
// and thus eliminates some weird "flickering"
// No precise math going on here.
zoomAccel = fabs(zoomAccel) < (0.01 * frameDelta) ? 0.0 : zoomAccel;
zoom += zoomAccel * fabs(zoom);
mouse.scroll = mouseScroll();
// Get the change in mouse coordinates, multiply by zoom, add to offset
if(glfwGetMouseButton( window.handle, GLFW_MOUSE_BUTTON_LEFT)) {
offsetCoords[0] -= (mouse.coords[0] - mouse.oldCoords[0]) /
(double)((window.width + window.height) / 2.0) *
zoom;
offsetCoords[1] += (mouse.coords[1] - mouse.oldCoords[1]) /
(double)((window.width + window.height) / 2.0) *
zoom;
glUniform2d(gl_data.shader_uniform.offset, offsetCoords[0], offsetCoords[1]);
}
iterChange = (int) (glfwGetKey(window.handle, GLFW_KEY_KP_ADD) > 0) + (int) (glfwGetKey(window.handle, GLFW_KEY_UP) > 0) ;
iterChange -= (int) (glfwGetKey(window.handle, GLFW_KEY_KP_SUBTRACT) > 0) + (int) (glfwGetKey(window.handle, GLFW_KEY_DOWN) > 0) ;
if(iterChange != 0) {
max_iterations += (int) (max(frameDelta, 1.0) * iterChange);
if(max_iterations < 0) {
max_iterations = 0;
}
glUniform1i(gl_data.shader_uniform.iter, max_iterations);
printf("Maximum iterations: %d\n", max_iterations);
}
// For polling reasons, this is flushed before rendering
mouseFlush();
keyFlush();
// All drawing calls, here we'll spend most of the time
renderFunc();
glfwPollEvents();
++framesPassed; // For FPS measurement
// Screenshot
if(keyHit( GLFW_KEY_F2 )) {
saveScreenshot(window.width, window.height);
}
// In case anyone wants to output the current coordinates
if(keyHit( GLFW_KEY_F3 )) {
printf("You are currently at: Re: %lf, Im: %lf\n", offsetCoords[0], offsetCoords[1]);
}
// Exit on Escape/Click on X
if( glfwGetKey( window.handle, GLFW_KEY_ESCAPE ) ||
glfwWindowShouldClose(window.handle))
{
isRunning = false;
}
}
glfwTerminate();
return 0;
};
void renderFunc() { // Main rendering function
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUniform1d(gl_data.shader_uniform.zoom, zoom);
glDrawRangeElements(GL_TRIANGLE_FAN, 0, 3, 4, GL_UNSIGNED_SHORT, NULL);
// Swap Back- and Frontbuffer, also polls input
glfwSwapBuffers(window.handle);
};
int initGraphics(int gfx_w, int gfx_h, int fullscreen, int disableVSync, int fsaa) {
if(!glfwInit()) {
fprintf(stderr, "Unable to initialize GLFW!\n");
}
GLFWmonitor* monitor = fullscreen ? glfwGetPrimaryMonitor() : NULL;
// Set some hints
glfwWindowHint( GLFW_CLIENT_API, GLFW_OPENGL_API );
glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); // We require OpenGL 3.2, which means older cards/Intel GPUs won't be able to run this.
glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 2 );
glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE );
glfwWindowHint( GLFW_RESIZABLE, GL_TRUE ); // Disallow window resizing (pain in the ass to handle)
if(fsaa) {
glfwWindowHint( GLFW_SAMPLES, fsaa );
}
/*
if(!glfwOpenWindow(
gfx_w, gfx_h, // Window Resolution
8, 8, 8, 8, // R,G,B,A bits (= 32bit depth in total)
0, 0, // depth- and stencilbuffer. We don't need those.
mode ) // Whether it should start in fullscreen or windowed mode
) {
fprintf(stderr, "Unable to open GLFW window.\n");
return 1;
}*/
window.handle = glfwCreateWindow(
gfx_w, gfx_h,
"Fractals are awesome!",
monitor,
NULL );
if( !(window.handle) ) {
fprintf(stderr, "Unable to open GLFW window.\n");
return 1;
}
glfwMakeContextCurrent(window.handle); // Protip: don't forget this.
// VSync off
if( disableVSync ) {
glfwSwapInterval(0);
} else {
glfwSwapInterval(1);
}
if(gl3wInit()) {
fprintf(stderr, "OpenGL failed to initialise.\n");
return 1;
}
if (!gl3wIsSupported(3, 2)) {
fprintf(stderr, "OpenGL Version 3.2 is not available on your system.\n");
return 1;
}
// Generate some ressources for OpenGL i.e. the quad and the 1D texture
generateQuad();
if(initTexture(&gl_data.color_palette, config.paletteFile)) {
fprintf(stderr, "Couldn't load '%s' for some reason.\n", config.paletteFile);
}
// Init shader objects & load shaders
initShader();
//loadShader(gl_data.frag_shader, "shader/fragtests.txt");
loadShader(gl_data.frag_shader, "shader/mandelbrot_unrolled.glsl");
loadShader(gl_data.vert_shader, "shader/vertexshader.glsl");
// Compile, link and error-check them
if(buildShader(gl_data.vert_shader, gl_data.frag_shader,
gl_data.prog_object) == 1)
{
return 1;
}
// Get the attribute position/uniform locations
gl_data.shader_attrib.position = glGetAttribLocation(gl_data.prog_object, "position"); // vertex position
gl_data.shader_uniform.zoom = glGetUniformLocation(gl_data.prog_object, "zoom"); // zoom value
gl_data.shader_uniform.ratio = glGetUniformLocation(gl_data.prog_object, "ratio"); // aspect ratio
gl_data.shader_uniform.offset = glGetUniformLocation(gl_data.prog_object, "offset"); // Offset coordinates
gl_data.shader_uniform.iter = glGetUniformLocation(gl_data.prog_object, "iter"); // Max iterations
return 0;
};
void generateQuad() {
const GLfloat vertex_data[] = {
-1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
1.0f, -1.0f
};
const GLushort element_data[] = { 0, 1, 2, 3 };
// Generate Vertex Buffer Object
glGenBuffers(1, &gl_data.vbo);
glBindBuffer(GL_ARRAY_BUFFER, gl_data.vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
// Generate Index Buffer Object
glGenBuffers(1, &gl_data.ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_data.ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW);
// Generate Vertex Array Object
glGenVertexArrays(1, &gl_data.vao);
glBindVertexArray(gl_data.vao);
};
int initShader() {
gl_data.prog_object = glCreateProgram();
gl_data.vert_shader = glCreateShader(GL_VERTEX_SHADER);
gl_data.frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
return 0;
};