3 #include "simulation.h"
10 #include <glm/ext.hpp>
11 #include <glm/glm.hpp>
15 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
24 Mode curMode = render;
29 const float metaballR = 2.f * 1.f / 16.f;
30 inline float metaballField(float r) {
33 const float a = r / metaballR;
34 return (-4.f / 9.f * powf(a, 6)) + (17.f / 9.f * powf(a, 4)) -
35 (22.f / 9.f * powf(a, 2)) + 1;
38 const float normalizationFactor = 748.f / 405.f * M_PI * metaballR;
41 if (GLenum e = glGetError()) {
42 fprintf(stderr, "%s\n", gluErrorString(e));
47 GLuint bbProg, sunProg;
50 // Here we need to generate n_q textures for different densities of metaballs
51 // These textures then go on the billboards
52 // The texture stores attenuation ratio?
57 // Stores attenuation ratio inside r channel
58 // Should be highest value at center
59 void precalculateBillboardTextures() {
60 fprintf(stderr, "Calculating billboard textures...\n");
61 glGenTextures(NQ, bbTexIds);
63 for (int d = 0; d < NQ; d++) {
65 for (int j = 0; j < 32; j++) {
66 for (int i = 0; i < 32; i++) {
67 // TODO: properly calculate this instead of whatever this is
68 float r = distance(vec2(i, j), vec2(16, 16)) / 32;
69 float density = (float)d / NQ;
71 1 - (density * 0.01 * metaballField(r * metaballR) / normalizationFactor);
77 snprintf(path, 32, "bbtex/%i.tga", d);
78 saveGrayscale(data, 32, 32, path);
80 glBindTexture(GL_TEXTURE_2D, bbTexIds[d]);
83 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_FLOAT, data);
84 glGenerateMipmap(GL_TEXTURE_2D); // required, otherwise texture is blank
88 fprintf(stderr, "\r%i out of %i densities calculated%s", d + 1, NQ,
89 d == NQ - 1 ? "\n" : "");
101 array<Metaball, CLOUD_DIM_X * CLOUD_DIM_Y * CLOUD_DIM_Z> metaballs;
105 void calculateMetaballs() {
107 /* for (int i = 0; i < 256; i++) { */
108 /* float x = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
109 /* float y = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
110 /* float z = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
111 /* float r = (float)rand()/(float)(RAND_MAX) * 1; */
112 /* Metaball m = {{x,y,z}, r}; */
113 /* metaballs.push_back(m); */
115 for (int i = 0; i < CLOUD_DIM_X; i++) {
116 for (int j = 0; j < CLOUD_DIM_Y; j++) {
117 for (int k = 0; k < CLOUD_DIM_Z; k++) {
118 const float cloudScale = 1.f / 16;
120 vec3(i, j, k) * vec3(cloudScale),
122 /* m.pos = (m.pos * vec3(2)) - (cloudScale / 2); */
123 m.pos -= vec3(CLOUD_DIM_X, CLOUD_DIM_Y, CLOUD_DIM_Z) * cloudScale / 2.f;
125 metaballs[i * CLOUD_DIM_Y * CLOUD_DIM_Z + j * CLOUD_DIM_Z + k] = m;
131 vec3 sunPos = {0, 5, 5}, sunDir = {0, -1, -1};
132 /* vec4 sunColor = {1,0,0.429,1}; */
133 vec4 sunColor = {1,1,1,1};
134 vec3 camPos = {0, 0, -5}, viewPos = {0, 0, 0};
135 mat4 proj; // projection matrix
136 mat4 view; // view matrix
137 float znear = 0.001, zfar = 1000;
138 float width = 600, height = 400;
139 float aspect = width / height;
141 void setProjectionAndViewUniforms(GLuint progId) {
142 GLuint projLoc = glGetUniformLocation(progId, "projection");
143 glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
145 GLuint viewLoc = glGetUniformLocation(progId, "view");
146 glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
149 /** Orientates the transformation matrix to face the camera in the view matrix
151 mat4 faceView(mat4 m) {
152 m[0][0] = view[0][0];
153 m[0][1] = view[1][0];
154 m[0][2] = view[2][0];
155 m[1][0] = view[0][1];
156 m[1][1] = view[1][1];
157 m[1][2] = view[2][1];
158 m[2][0] = view[0][2];
159 m[2][1] = view[1][2];
160 m[2][2] = view[2][2];
164 GLuint attenuationTex;
166 const float metaballScale = metaballR * 1.4f;
169 glDisable(GL_DEPTH_TEST);
170 // shaderOutput * 0 + buffer * shader alpha
171 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
174 // sort by ascending distance from the sun
175 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
176 return distance(sunPos, a.pos) < distance(sunPos, b.pos);
179 glActiveTexture(GL_TEXTURE0);
180 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
182 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
183 glUniform1i(glGetUniformLocation(bbProg, "debug"), 0);
185 for (auto &k : metaballs) {
186 // place the billboard at the center of k
187 mat4 model = translate(mat4(1), k.pos);
189 // rotate the billboard so that its normal is oriented to the sun
190 model = faceView(model);
192 model = scale(model, vec3(metaballScale));
194 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
196 // Set the billboard color as RGBA = (1.0, 1.0, 1.0, 1.0).
197 vec4 color = {1, 1, 1, 1};
198 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
199 glm::value_ptr(color));
201 // Map the billboard texture with GL_MODULATE.
202 // i.e. multiply rather than add
203 // but glTexEnv is for the old fixed function pipeline --
204 // need to just tell our fragment shader then to modulate
206 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
207 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 1);
209 // Render the billboard.
210 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
212 // Read the pixel value corresponding to the center of metaball k.
213 // 1. First get position in opengl screen space: from [-1,1]
214 // 2. Normalize to [0,1]
215 // 3. Multiply by (width * height)
217 ((vec2(proj * view * model * vec4(0, 0, 0, 1)) + vec2(1)) / vec2(2)) *
220 // TODO: This is a huge bottleneck
221 glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT,
224 // Multiply the pixel value by the sunlight color.
227 // Store the color into an array C[k] as the color of the billboard.
235 void renderObject() {
238 glUseProgram(sunProg);
239 mat4 model = translate(mat4(1), sunPos);
240 /* model = lookAt(sunPos, sunPos + sunDir, {0, 1, 0}) * model; */
241 model = translate(scale(translate(model, -sunPos), vec3(0.3)), sunPos);
242 glUniformMatrix4fv(glGetUniformLocation(sunProg, "model"), 1, GL_FALSE, glm::value_ptr(model));
243 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
246 void renderClouds() {
247 glUseProgram(bbProg);
249 // Sort metaballs in descending order from the viewpoint
250 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
251 return distance(camPos, a.pos) > distance(camPos, b.pos);
254 glUniform1i(glGetUniformLocation(bbProg, "debug"), curMode != render);
256 glDisable(GL_DEPTH_TEST);
258 // shaderOutput * 1 + buffer * shader alpha
259 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
261 glActiveTexture(GL_TEXTURE0);
262 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
264 for (int i = 0; i < metaballs.size(); i++) {
265 Metaball k = metaballs[i];
267 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
269 // Place the billboard at the center of the corresponding metaball n.
270 mat4 model = translate(mat4(1), k.pos);
271 // Rotate the billboard so that its normal is oriented to the viewpoint.
272 model = faceView(model);
274 model = scale(model, vec3(metaballScale));
276 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
278 // Set the billboard color as C[n].
280 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
281 glm::value_ptr(k.col));
283 // Map the billboard texture.
285 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
287 // Don't modulate it -- blend it
288 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 0);
290 glUniform1f(glGetUniformLocation(bbProg, "debugColor"), curMode == debugColor);
291 if (curMode != render) {
293 if (curMode == debugContDist) debugVal = k.d;
294 else if (curMode == debugProbAct) debugVal = cs.p_act[k.coords.x][k.coords.y][k.coords.z];
295 else if (curMode == debugProbExt) debugVal = cs.p_ext[k.coords.x][k.coords.y][k.coords.z];
296 glUniform1f(glGetUniformLocation(bbProg, "debugVal"), debugVal);
298 model = scale(model, vec3(0.2));
299 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
302 // Render the billboard with the blending function.
303 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
307 bool needsReshading = true;
309 if (needsReshading) {
310 // TODO: find a way to make sure there's no clipping
311 view = glm::lookAt(sunPos + sunDir * vec3(20), sunPos, {0, 1, 0});
312 proj = glm::ortho(1.f * aspect, -1.f * aspect, -1.f, 1.f, znear, zfar);
313 setProjectionAndViewUniforms(bbProg);
315 glClearColor(1, 1, 1, 1);
316 glClear(GL_COLOR_BUFFER_BIT);
318 needsReshading = false;
321 view = glm::lookAt(camPos, viewPos, {0, 1, 0});
322 proj = glm::perspective(45.f, aspect, znear, zfar);
323 glUseProgram(sunProg);
324 setProjectionAndViewUniforms(sunProg);
325 glUseProgram(bbProg);
326 setProjectionAndViewUniforms(bbProg);
328 glClearColor(0.83, 1, 1, 1); // background color
329 glClear(GL_COLOR_BUFFER_BIT);
330 renderObject(); // render things that aren't clouds
336 bool needsRedisplay = false;
338 /* calculateMetaballs(); */
339 if (needsRedisplay) {
342 needsRedisplay = false;
343 glutTimerFunc(16, timer, 0);
346 void keyboard(unsigned char key, int x, int y) {
348 calculateMetaballs();
349 needsRedisplay = true;
350 needsReshading = curMode == render;
354 needsRedisplay = true;
357 curMode = debugContDist;
358 needsRedisplay = true;
361 curMode = debugColor;
362 needsRedisplay = true;
365 curMode = debugProbAct;
366 needsRedisplay = true;
369 curMode = debugProbExt;
370 needsRedisplay = true;
374 int prevMouseX, prevMouseY;
375 bool firstMouse = true;
376 void motion(int x, int y) {
382 float dx = x - prevMouseX, dy = y - prevMouseY;
385 const vec3 origin(0, 0, 0);
386 const float sensitivity = 0.003f;
387 auto camMat = translate(mat4(1), origin + camPos);
388 auto rotation = rotate(rotate(mat4(1), -dx * sensitivity, {0, 1, 0}),
389 -dy * sensitivity, {1, 0, 0});
390 auto rotAroundOrig = camMat * rotation * translate(mat4(1), origin - camPos);
391 camPos = rotAroundOrig * glm::vec4(camPos, 0);
392 needsRedisplay = true;
395 void passiveMotion(int x, int y) {
400 int main(int argc, char **argv) {
401 glutInit(&argc, argv);
402 glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB |
403 GLUT_3_2_CORE_PROFILE);
404 glutInitWindowSize(width, height);
405 glutCreateWindow("Clouds");
406 glutDisplayFunc(display);
410 Program prog("billboardvert.glsl", "billboardfrag.glsl");
411 bbProg = prog.progId;
412 Program sProg("sunvert.glsl", "sunfrag.glsl");
413 sunProg = sProg.progId;
415 glGenVertexArrays(1, &bbVao);
416 glUseProgram(sunProg);
417 glBindVertexArray(bbVao);
418 glUseProgram(bbProg);
419 glBindVertexArray(bbVao);
421 glGenBuffers(2, vbos);
423 vector<vec3> poss = {{-1, -1, 0}, {-1, 1, 0}, {1, 1, 0}, {1, -1, 0}};
424 vector<GLuint> indices = {2, 1, 0, 3, 2, 0};
426 GLuint posLoc = glGetAttribLocation(bbProg, "vPosition");
427 glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
428 glBufferData(GL_ARRAY_BUFFER, poss.size() * sizeof(glm::vec3), &poss[0],
430 glEnableVertexAttribArray(posLoc);
431 glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
433 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
434 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
435 &indices[0], GL_STATIC_DRAW);
439 precalculateBillboardTextures();
442 calculateMetaballs();
444 glGenTextures(1, &attenuationTex);
446 glutKeyboardFunc(keyboard);
447 glutMotionFunc(motion);
448 glutPassiveMotionFunc(passiveMotion);
449 glutTimerFunc(16, timer, 0);
451 // set up billboard prog