cubemapper

view src/app.cc @ 4:2bfafdced01a

added README, COPYING, and copyright headers
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 30 Jul 2017 16:11:19 +0300
parents f5cc465eb735
children 614295b72341
line source
1 /*
2 Cubemapper - a program for converting panoramic images into cubemaps
3 Copyright (C) 2017 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
22 #include <assert.h>
23 #include <imago2.h>
24 #include "app.h"
25 #include "opengl.h"
26 #include "texture.h"
27 #include "mesh.h"
28 #include "meshgen.h"
30 static void draw_equilateral();
31 static void draw_cubemap();
32 static bool parse_args(int argc, char **argv);
34 static void flip_image(float *pixels, int xsz, int ysz);
36 static const char *img_fname, *img_suffix;
37 static float cam_theta, cam_phi;
39 static Texture *pano_tex;
40 static Mesh *pano_mesh;
42 static int win_width, win_height;
43 static int show_cubemap;
45 static unsigned int fbo;
46 static unsigned int cube_tex;
47 static int cube_size;
50 bool app_init(int argc, char **argv)
51 {
52 if(!parse_args(argc, argv)) {
53 return false;
54 }
55 if(!img_fname) {
56 fprintf(stderr, "please specify an equilateral panoramic image\n");
57 return false;
58 }
60 if(!init_opengl()) {
61 return false;
62 }
64 glEnable(GL_MULTISAMPLE);
66 Mesh::use_custom_sdr_attr = false;
67 pano_mesh = new Mesh;
68 gen_sphere(pano_mesh, 1.0, 80, 40);
69 pano_mesh->flip();
70 Mat4 xform;
71 xform.rotation_y(-M_PI / 2.0); // rotate the sphere to face the "front" part of the image
72 pano_mesh->apply_xform(xform, xform);
74 xform.scaling(-1, 1, 1); // flip horizontal texcoord since we're inside the sphere
75 pano_mesh->texcoord_apply_xform(xform);
77 pano_tex = new Texture;
78 if(!pano_tex->load(img_fname)) {
79 return false;
80 }
81 printf("loaded image: %dx%d\n", pano_tex->get_width(), pano_tex->get_height());
83 if(!(img_suffix = strrchr(img_fname, '.'))) {
84 img_suffix = ".jpg";
85 }
87 // create cubemap
88 cube_size = pano_tex->get_height();
89 glGenTextures(1, &cube_tex);
90 glBindTexture(GL_TEXTURE_CUBE_MAP, cube_tex);
91 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
92 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
93 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
94 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
95 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
97 for(int i=0; i<6; i++) {
98 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, cube_size, cube_size,
99 0, GL_RGB, GL_FLOAT, 0);
100 }
103 // create fbo
104 glGenFramebuffers(1, &fbo);
106 // tex-gen for cubemap visualization
107 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
108 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
109 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
110 float planes[][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}};
111 glTexGenfv(GL_S, GL_OBJECT_PLANE, planes[0]);
112 glTexGenfv(GL_T, GL_OBJECT_PLANE, planes[1]);
113 glTexGenfv(GL_R, GL_OBJECT_PLANE, planes[2]);
114 return true;
115 }
117 void app_cleanup()
118 {
119 delete pano_mesh;
120 delete pano_tex;
121 }
123 void app_draw()
124 {
125 glClear(GL_COLOR_BUFFER_BIT);
127 Mat4 view_matrix;
128 view_matrix.pre_rotate_x(deg_to_rad(cam_phi));
129 view_matrix.pre_rotate_y(deg_to_rad(cam_theta));
131 glMatrixMode(GL_MODELVIEW);
132 glLoadMatrixf(view_matrix[0]);
134 if(show_cubemap) {
135 draw_cubemap();
137 glColor3f(0, 0, 0);
138 app_print_text(10, 10, "cubemap");
139 glColor3f(0, 0.8, 1);
140 app_print_text(8, 13, "cubemap");
141 } else {
142 draw_equilateral();
144 glColor3f(0, 0, 0);
145 app_print_text(10, 10, "equilateral");
146 glColor3f(1, 0.8, 0);
147 app_print_text(8, 13, "equilateral");
148 }
149 glColor3f(1, 1, 1);
151 app_swap_buffers();
152 assert(glGetError() == GL_NO_ERROR);
153 }
155 void render_cubemap()
156 {
157 printf("rendering cubemap %dx%d\n", cube_size, cube_size);
159 float *pixels = new float[cube_size * cube_size * 3];
161 glViewport(0, 0, cube_size, cube_size);
163 Mat4 viewmat[6];
164 viewmat[0].rotation_y(deg_to_rad(90)); // +X
165 viewmat[1].rotation_y(deg_to_rad(-90)); // -X
166 viewmat[2].rotation_x(deg_to_rad(90)); // +Y
167 viewmat[2].rotate_y(deg_to_rad(180));
168 viewmat[3].rotation_x(deg_to_rad(-90)); // -Y
169 viewmat[3].rotate_y(deg_to_rad(180));
170 viewmat[4].rotation_y(deg_to_rad(180)); // +Z
172 // this must coincide with the order of GL_TEXTURE_CUBE_MAP_* values
173 static const char *fname_pattern[] = {
174 "cubemap_px%s",
175 "cubemap_nx%s",
176 "cubemap_py%s",
177 "cubemap_ny%s",
178 "cubemap_pz%s",
179 "cubemap_nz%s"
180 };
181 static char fname[64];
183 glMatrixMode(GL_PROJECTION);
184 glPushMatrix();
185 glLoadIdentity();
186 gluPerspective(90, 1.0, 0.5, 500.0);
187 glScalef(-1, -1, 1);
189 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
191 for(int i=0; i<6; i++) {
192 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
193 GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cube_tex, 0);
195 glClear(GL_COLOR_BUFFER_BIT);
197 glMatrixMode(GL_MODELVIEW);
198 glLoadMatrixf(viewmat[i][0]);
200 draw_equilateral();
202 //glReadPixels(0, 0, cube_size, cube_size, GL_RGB, GL_FLOAT, pixels);
203 glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, GL_FLOAT, pixels);
204 //flip_image(pixels, cube_size, cube_size);
206 sprintf(fname, fname_pattern[i], img_suffix);
207 if(img_save_pixels(fname, pixels, cube_size, cube_size, IMG_FMT_RGBF) == -1) {
208 fprintf(stderr, "failed to save %dx%d image: %s\n", cube_size, cube_size, fname);
209 }
210 }
212 glBindFramebuffer(GL_FRAMEBUFFER, 0);
213 glViewport(0, 0, win_width, win_height);
215 glMatrixMode(GL_PROJECTION);
216 glPopMatrix();
218 delete [] pixels;
220 glBindTexture(GL_TEXTURE_CUBE_MAP, cube_tex);
221 glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
222 }
224 static void draw_equilateral()
225 {
226 pano_tex->bind();
227 glEnable(GL_TEXTURE_2D);
228 pano_mesh->draw();
229 glDisable(GL_TEXTURE_2D);
230 }
232 static void draw_cubemap()
233 {
234 glPushAttrib(GL_ENABLE_BIT);
236 glBindTexture(GL_TEXTURE_CUBE_MAP, cube_tex);
237 glEnable(GL_TEXTURE_CUBE_MAP);
238 glEnable(GL_TEXTURE_GEN_S);
239 glEnable(GL_TEXTURE_GEN_T);
240 glEnable(GL_TEXTURE_GEN_R);
242 pano_mesh->draw();
244 glPopAttrib();
245 }
247 void app_reshape(int x, int y)
248 {
249 glViewport(0, 0, x, y);
251 glMatrixMode(GL_PROJECTION);
252 glLoadIdentity();
253 gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
255 win_width = x;
256 win_height = y;
257 }
259 void app_keyboard(int key, bool press)
260 {
261 if(press) {
262 switch(key) {
263 case 27:
264 app_quit();
265 break;
267 case ' ':
268 show_cubemap = !show_cubemap;
269 app_redisplay();
270 break;
272 case 'c':
273 render_cubemap();
274 break;
275 }
276 }
277 }
279 static float prev_x, prev_y;
280 static bool bnstate[16];
282 void app_mouse_button(int bn, bool press, int x, int y)
283 {
284 if(bn < (int)(sizeof bnstate / sizeof *bnstate)) {
285 bnstate[bn] = press;
286 }
287 prev_x = x;
288 prev_y = y;
289 }
291 void app_mouse_motion(int x, int y)
292 {
293 float dx = x - prev_x;
294 float dy = y - prev_y;
295 prev_x = x;
296 prev_y = y;
298 if(!dx && !dy) return;
300 if(bnstate[0]) {
301 cam_theta += dx * 0.5;
302 cam_phi += dy * 0.5;
304 if(cam_phi < -90) cam_phi = -90;
305 if(cam_phi > 90) cam_phi = 90;
306 app_redisplay();
307 }
308 }
310 static bool parse_args(int argc, char **argv)
311 {
312 for(int i=1; i<argc; i++) {
313 if(argv[i][0] == '-') {
314 /*
315 } else if(strcmp(argv[i], "-help") == 0) {
316 printf("usage: %s [options]\noptions:\n", argv[0]);
317 printf(" -help: print usage information and exit\n");
318 exit(0);
319 } else {*/
320 fprintf(stderr, "invalid option: %s\n", argv[i]);
321 return false;
322 //}
323 } else {
324 if(img_fname) {
325 fprintf(stderr, "unexpected option: %s\n", argv[i]);
326 return false;
327 }
328 img_fname = argv[i];
329 }
330 }
332 return true;
333 }
335 static void flip_image(float *pixels, int xsz, int ysz)
336 {
337 float *top_ptr = pixels;
338 float *bot_ptr = pixels + xsz * (ysz - 1) * 3;
339 float *line = new float[xsz * 3];
340 int scansz = xsz * 3 * sizeof(float);
342 for(int i=0; i<ysz / 2; i++) {
343 memcpy(line, top_ptr, scansz);
344 memcpy(top_ptr, bot_ptr, scansz);
345 memcpy(bot_ptr, line, scansz);
346 top_ptr += xsz * 3;
347 bot_ptr -= xsz * 3;
348 }
350 delete [] line;
351 }