cubemapper

diff src/app.cc @ 0:8fc9e1d3aad2

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 27 Jul 2017 20:36:12 +0300
parents
children d7a29cb7ac8d
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/app.cc	Thu Jul 27 20:36:12 2017 +0300
     1.3 @@ -0,0 +1,243 @@
     1.4 +#include <stdio.h>
     1.5 +#include <stdlib.h>
     1.6 +#include <string.h>
     1.7 +#include <math.h>
     1.8 +#include <assert.h>
     1.9 +#include <imago2.h>
    1.10 +#include "app.h"
    1.11 +#include "opengl.h"
    1.12 +#include "texture.h"
    1.13 +#include "mesh.h"
    1.14 +#include "meshgen.h"
    1.15 +
    1.16 +static void draw_scene();	// both near and infinite parts
    1.17 +static void draw_scene_near();	// near scene: regular objects affected by parallax shift and translation
    1.18 +// infinity scene: objects conceptually at infinity, not affected by parallax shift and translation
    1.19 +static void draw_scene_inf();
    1.20 +static bool parse_args(int argc, char **argv);
    1.21 +
    1.22 +static const char *img_fname;
    1.23 +static float cam_theta, cam_phi;
    1.24 +
    1.25 +static Texture *pano_tex;
    1.26 +static Mesh *pano_mesh;
    1.27 +
    1.28 +static int win_width, win_height;
    1.29 +
    1.30 +
    1.31 +bool app_init(int argc, char **argv)
    1.32 +{
    1.33 +	if(!parse_args(argc, argv)) {
    1.34 +		return false;
    1.35 +	}
    1.36 +	if(!img_fname) {
    1.37 +		fprintf(stderr, "please specify an equilateral panoramic image\n");
    1.38 +		return false;
    1.39 +	}
    1.40 +
    1.41 +	if(!init_opengl()) {
    1.42 +		return false;
    1.43 +	}
    1.44 +
    1.45 +	glEnable(GL_CULL_FACE);
    1.46 +
    1.47 +	if(GLEW_ARB_framebuffer_sRGB) {
    1.48 +		glGetError();	// discard previous errors
    1.49 +		glEnable(GL_FRAMEBUFFER_SRGB);
    1.50 +		if(glGetError() != GL_NO_ERROR) {
    1.51 +			fprintf(stderr, "failed to enable sRGB framebuffer\n");
    1.52 +		}
    1.53 +	}
    1.54 +
    1.55 +	Mesh::use_custom_sdr_attr = false;
    1.56 +	pano_mesh = new Mesh;
    1.57 +	gen_sphere(pano_mesh, 1.0, 80, 40);
    1.58 +	pano_mesh->flip();
    1.59 +	Mat4 xform;
    1.60 +	xform.rotation_y(-M_PI / 2.0);	// rotate the sphere to face the "front" part of the image
    1.61 +	pano_mesh->apply_xform(xform, xform);
    1.62 +
    1.63 +	xform.scaling(-1, 1, 1);		// flip horizontal texcoord since we're inside the sphere
    1.64 +	pano_mesh->texcoord_apply_xform(xform);
    1.65 +
    1.66 +	pano_tex = new Texture;
    1.67 +	if(!pano_tex->load(img_fname)) {
    1.68 +		return false;
    1.69 +	}
    1.70 +	return true;
    1.71 +}
    1.72 +
    1.73 +void app_cleanup()
    1.74 +{
    1.75 +	delete pano_mesh;
    1.76 +	delete pano_tex;
    1.77 +}
    1.78 +
    1.79 +void app_draw()
    1.80 +{
    1.81 +	glClear(GL_COLOR_BUFFER_BIT);
    1.82 +
    1.83 +	Mat4 view_matrix;
    1.84 +	view_matrix.pre_rotate_x(deg_to_rad(cam_phi));
    1.85 +	view_matrix.pre_rotate_y(deg_to_rad(cam_theta));
    1.86 +
    1.87 +	glMatrixMode(GL_MODELVIEW);
    1.88 +	glLoadMatrixf(view_matrix[0]);
    1.89 +
    1.90 +	draw_scene();
    1.91 +
    1.92 +	app_swap_buffers();
    1.93 +	assert(glGetError() == GL_NO_ERROR);
    1.94 +}
    1.95 +
    1.96 +void render_cubemap()
    1.97 +{
    1.98 +	int fbsize = win_width < win_height ? win_width : win_height;
    1.99 +	float *pixels = new float[fbsize * fbsize * 3];
   1.100 +
   1.101 +	glViewport(0, 0, fbsize, fbsize);
   1.102 +
   1.103 +	Mat4 viewmat[6];
   1.104 +	viewmat[0].rotation_y(deg_to_rad(90));	// +X
   1.105 +	viewmat[1].rotation_x(deg_to_rad(-90));	// +Y
   1.106 +	viewmat[2].rotation_y(deg_to_rad(180));	// +Z
   1.107 +	viewmat[3].rotation_y(deg_to_rad(-90));	// -X
   1.108 +	viewmat[4].rotation_x(deg_to_rad(90));	// -Y
   1.109 +
   1.110 +	static const char *fname[] = {
   1.111 +		"cubemap_px.jpg",
   1.112 +		"cubemap_py.jpg",
   1.113 +		"cubemap_pz.jpg",
   1.114 +		"cubemap_nx.jpg",
   1.115 +		"cubemap_ny.jpg",
   1.116 +		"cubemap_nz.jpg"
   1.117 +	};
   1.118 +
   1.119 +	glMatrixMode(GL_PROJECTION);
   1.120 +	glLoadIdentity();
   1.121 +	gluPerspective(45, 1.0, 0.5, 500.0);
   1.122 +
   1.123 +	for(int i=0; i<6; i++) {
   1.124 +		glClear(GL_COLOR_BUFFER_BIT);
   1.125 +
   1.126 +		glMatrixMode(GL_MODELVIEW);
   1.127 +		glLoadMatrixf(viewmat[i][0]);
   1.128 +
   1.129 +		draw_scene();
   1.130 +
   1.131 +		glReadPixels(0, 0, fbsize, fbsize, GL_RGB, GL_FLOAT, pixels);
   1.132 +		if(img_save_pixels(fname[i], pixels, fbsize, fbsize, IMG_FMT_RGBF) == -1) {
   1.133 +			fprintf(stderr, "failed to save %dx%d image: %s\n", fbsize, fbsize, fname[i]);
   1.134 +			break;
   1.135 +		}
   1.136 +	}
   1.137 +
   1.138 +	glViewport(0, 0, win_width, win_height);
   1.139 +
   1.140 +	delete [] pixels;
   1.141 +}
   1.142 +
   1.143 +// both near and infinite parts (see below)
   1.144 +static void draw_scene()
   1.145 +{
   1.146 +	draw_scene_inf();
   1.147 +	draw_scene_near();
   1.148 +}
   1.149 +
   1.150 +// infinity scene: objects conceptually at infinity, not affected by parallax shift and translation
   1.151 +static void draw_scene_inf()
   1.152 +{
   1.153 +	pano_tex->bind();
   1.154 +	glEnable(GL_TEXTURE_2D);
   1.155 +	pano_mesh->draw();
   1.156 +	glDisable(GL_TEXTURE_2D);
   1.157 +}
   1.158 +
   1.159 +// near scene: regular objects affected by parallax shift and translation
   1.160 +static void draw_scene_near()
   1.161 +{
   1.162 +}
   1.163 +
   1.164 +void app_reshape(int x, int y)
   1.165 +{
   1.166 +	glViewport(0, 0, x, y);
   1.167 +
   1.168 +	glMatrixMode(GL_PROJECTION);
   1.169 +	glLoadIdentity();
   1.170 +	gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
   1.171 +
   1.172 +	win_width = x;
   1.173 +	win_height = y;
   1.174 +}
   1.175 +
   1.176 +void app_keyboard(int key, bool press)
   1.177 +{
   1.178 +	if(press) {
   1.179 +		switch(key) {
   1.180 +		case 27:
   1.181 +			app_quit();
   1.182 +			break;
   1.183 +
   1.184 +		case 's':
   1.185 +			printf("rendering cubemap\n");
   1.186 +			render_cubemap();
   1.187 +			break;
   1.188 +		}
   1.189 +	}
   1.190 +}
   1.191 +
   1.192 +static float prev_x, prev_y;
   1.193 +static bool bnstate[16];
   1.194 +
   1.195 +void app_mouse_button(int bn, bool press, int x, int y)
   1.196 +{
   1.197 +	if(bn < (int)(sizeof bnstate / sizeof *bnstate)) {
   1.198 +		bnstate[bn] = press;
   1.199 +	}
   1.200 +	prev_x = x;
   1.201 +	prev_y = y;
   1.202 +}
   1.203 +
   1.204 +void app_mouse_motion(int x, int y)
   1.205 +{
   1.206 +	float dx = x - prev_x;
   1.207 +	float dy = y - prev_y;
   1.208 +	prev_x = x;
   1.209 +	prev_y = y;
   1.210 +
   1.211 +	if(!dx && !dy) return;
   1.212 +
   1.213 +	if(bnstate[0]) {
   1.214 +		cam_theta += dx * 0.5;
   1.215 +		cam_phi += dy * 0.5;
   1.216 +
   1.217 +		if(cam_phi < -90) cam_phi = -90;
   1.218 +		if(cam_phi > 90) cam_phi = 90;
   1.219 +		app_redisplay();
   1.220 +	}
   1.221 +}
   1.222 +
   1.223 +static bool parse_args(int argc, char **argv)
   1.224 +{
   1.225 +	for(int i=1; i<argc; i++) {
   1.226 +		if(argv[i][0] == '-') {
   1.227 +			/*
   1.228 +			} else if(strcmp(argv[i], "-help") == 0) {
   1.229 +				printf("usage: %s [options]\noptions:\n", argv[0]);
   1.230 +				printf(" -help: print usage information and exit\n");
   1.231 +				exit(0);
   1.232 +			} else {*/
   1.233 +				fprintf(stderr, "invalid option: %s\n", argv[i]);
   1.234 +				return false;
   1.235 +			//}
   1.236 +		} else {
   1.237 +			if(img_fname) {
   1.238 +				fprintf(stderr, "unexpected option: %s\n", argv[i]);
   1.239 +				return false;
   1.240 +			}
   1.241 +			img_fname = argv[i];
   1.242 +		}
   1.243 +	}
   1.244 +
   1.245 +	return true;
   1.246 +}