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