openglcanvas.cpp 27.8 KB
Newer Older
Luis Peñaranda's avatar
Luis Peñaranda committed
1 2
#include <QtGui>
#ifdef NEEDGLEE
3
  #include "GLee.h"
Luis Peñaranda's avatar
Luis Peñaranda committed
4 5 6
#endif
#include "openglcanvas.h"
#include <cmath>
7 8
#include <sys/types.h>  // for fstat, getpwuid, getuid
#include <sys/stat.h>   // for fstat
Luis Penaranda's avatar
Luis Penaranda committed
9 10 11 12
#ifdef _WIN32
  #include <stdlib.h>   // for getenv (TODO: use getenv_s)
#else
  #include <pwd.h>      // for getpwuid
13
#endif
14 15
#include <cstdio>       // for fopen, fclose, getc
#include <cstring>
Luis Penaranda's avatar
Luis Penaranda committed
16
#ifdef _MSC_VER
17 18
  #include <direct.h>
  #define GET_WORKDIR _getcwd
19 20
  #define OPEN_FILE _open
  #define LSEEK_FD _lseek
Luis Penaranda's avatar
Luis Penaranda committed
21 22 23 24 25
  #define FOPEN_RO(_descriptor,_filename) \
    if(fopen_s(&(_descriptor),_filename,"r")){ \
        fprintf(stderr,"unable to open file '%s'\n",_filename); \
        exit(-1); \
    }
26
#else
27
  // unistd was included above
28
  #define GET_WORKDIR getcwd
29 30
  #define OPEN_FILE open
  #define LSEEK_FD lseek
Luis Penaranda's avatar
Luis Penaranda committed
31
  #define FOPEN_RO(_descriptor,_filename) \
Luis Penaranda's avatar
Luis Penaranda committed
32 33
    _descriptor=fopen(_filename,"r"); \
    if((_descriptor)==NULL){ \
Luis Penaranda's avatar
Luis Penaranda committed
34 35 36
        fprintf(stderr,"unable to open file '%s'\n",_filename); \
        exit(-1); \
    }
37
#endif
Luis Peñaranda's avatar
Luis Peñaranda committed
38

39 40 41 42
#ifndef _MSC_VER
  #define PROGNAME "pano_interface"
#endif

43 44
#define VERT_SHADER_FILE "test_vertex_shader.vert"
#define FRAG_SHADER_FILE "fragment_shader.frag"
Luis Peñaranda's avatar
Luis Peñaranda committed
45

46 47 48 49 50 51 52 53
#define CONST_PI        3.141592653589793115997963468544185161590576171875
#define CONST_PI_2      1.5707963267948965579989817342720925807952880859375
#define CONST_PI_F      3.1415927410125732421875f
#define CONST_PI_2_F    1.57079637050628662109375f
//#define CONST_PI        (0x1.921fb54442d18p+1)
//#define CONST_PI_2      (0x1.921fb54442d18p+0)
//#define CONST_PI_F      (0x1.921fb6p+1f)
//#define CONST_PI_2_F    (0x1.921fb6p+0f)
Luis Peñaranda's avatar
Luis Peñaranda committed
54

Luis Peñaranda's avatar
Luis Peñaranda committed
55 56 57 58 59
OpenGLCanvas::OpenGLCanvas(QWidget *parent) :
    QGLWidget(parent)
{
    setFormat(QGL::DoubleBuffer | QGL::DepthBuffer);
    fov = 60.f;
Luis Peñaranda's avatar
Luis Peñaranda committed
60
    fov_max = 60.f; // TODO: I only use floats here because Leo did... check
Luis Peñaranda's avatar
Luis Peñaranda committed
61 62 63 64 65
    scale = 1.0f;
    center_lambda = 0.f;
    center_phi = 0.f;
    fov_scale_relation = "Square Root";
    visualization = "Perspective";
66
    auto_fov_max=false;
Luis Peñaranda's avatar
Luis Peñaranda committed
67 68 69 70 71 72 73 74 75 76 77 78 79

    time_frames = 0;
    time_timer.setInterval(0);
    connect(&time_timer, SIGNAL(timeout()), this, SLOT(slotTimer()));
    time_start = time_time.time();
}

void OpenGLCanvas::slotTimer(void) {
    updateGL();
}

void OpenGLCanvas::change_fov(double f){

Luis Peñaranda's avatar
Luis Peñaranda committed
80 81 82 83
    if (fov!=f && f>=1.f && f<=360.f)
        fov=f;
    if (fov<=fov_max)
        scale=1.f;
Luis Peñaranda's avatar
Luis Peñaranda committed
84 85
    //else if (fov>295.f)
    //    scale = 0.02f; // TODO: check this value wrt fov_max
Luis Peñaranda's avatar
Luis Peñaranda committed
86
    else {
Luis Peñaranda's avatar
Luis Peñaranda committed
87 88 89 90 91 92 93 94 95 96 97 98
        if (fov_scale_relation == "Naive")
            scale=fov_max/fov;
        else if (fov_scale_relation == "Square Root")
            scale=sqrt((360.f-fov_max-fov)/(360.-2*fov_max));
        else if (fov_scale_relation == "Linear")
            scale=(360.f-fov_max-fov)/(360.-2*fov_max);
        else if (fov_scale_relation == "Square Power")
            scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),2);
        else if (fov_scale_relation == "Cubic Power")
            scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),3);
        else if (fov_scale_relation == "Logarithm")
            scale=log(exp(1.f)+(1.f-exp(1.f))*(fov-fov_max)/(360.-2*fov_max));
Luis Peñaranda's avatar
Luis Peñaranda committed
99 100 101 102
    }

//    scale = 0.3f;

Luis Peñaranda's avatar
Luis Peñaranda committed
103
    fprintf(stderr,"change fov, fov=%f, fov_max=%f, scale=%f\n",fov,fov_max,scale);
104
    emit fov_changed((int)fov);
Luis Peñaranda's avatar
Luis Peñaranda committed
105

Luis Peñaranda's avatar
Luis Peñaranda committed
106 107 108 109
    updateGL();

}

110
void OpenGLCanvas::change_fov(int new_fov){
Luis Peñaranda's avatar
Luis Peñaranda committed
111 112
    if(new_fov<=360&&new_fov>=1)
            change_fov((double)new_fov);
113 114 115 116 117 118 119 120 121
    if(auto_fov_max){
        if(new_fov<60)
            change_fov_max(60);
        else
            if(new_fov>180)
                change_fov_max(1);
            else
                change_fov_max(90-new_fov/2);
    }
122 123 124
}

void OpenGLCanvas::change_fov_max(int new_fov_max){
Luis Peñaranda's avatar
Luis Peñaranda committed
125
    if(new_fov_max<=360&&new_fov_max>=1)
Luis Peñaranda's avatar
Luis Peñaranda committed
126
            fov_max=(double)new_fov_max;
127 128
    if (fov<=fov_max)
        scale=1.f;
Luis Peñaranda's avatar
Luis Peñaranda committed
129 130
    //else if (fov>295.f)
    //    scale = 0.02f; // TODO: check this value wrt fov_max
131 132 133 134 135 136 137 138 139 140 141 142 143 144
    else {
        if (fov_scale_relation == "Naive")
            scale=fov_max/fov;
        else if (fov_scale_relation == "Square Root")
            scale=sqrt((360.f-fov_max-fov)/(360.-2*fov_max));
        else if (fov_scale_relation == "Linear")
            scale=(360.f-fov_max-fov)/(360.-2*fov_max);
        else if (fov_scale_relation == "Square Power")
            scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),2);
        else if (fov_scale_relation == "Cubic Power")
            scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),3);
        else if (fov_scale_relation == "Logarithm")
            scale=log(exp(1.f)+(1.f-exp(1.f))*(fov-fov_max)/(360.-2*fov_max));
    }
Luis Peñaranda's avatar
Luis Peñaranda committed
145
    fprintf(stderr,"change fov_max, fov=%f, fov_max=%f, new scale=%f\n",fov,fov_max,scale);
146
    emit max_fov_changed((int)fov_max);
Luis Peñaranda's avatar
Luis Peñaranda committed
147 148 149
    updateGL();
}

Luis Peñaranda's avatar
Luis Peñaranda committed
150 151 152 153 154 155 156 157 158
//void OpenGLCanvas::change_scale(double s){

//    if (scale!=s && s>=0.0 && s<=1.0) scale = s;
//    updateGL();

//}

void OpenGLCanvas::change_center_lambda(double lambda){

Luis Peñaranda's avatar
Luis Peñaranda committed
159
    if (center_lambda!=lambda && lambda>=-CONST_PI && lambda<=CONST_PI) {
Luis Peñaranda's avatar
Luis Peñaranda committed
160 161 162 163 164 165 166 167
        center_lambda = lambda;
        updateGL();
    }

}

void OpenGLCanvas::change_center_phi(double phi){

Luis Peñaranda's avatar
Luis Peñaranda committed
168
    if (center_phi!=phi && phi>=-CONST_PI_2 && phi<=CONST_PI_2) {
Luis Peñaranda's avatar
Luis Peñaranda committed
169 170 171 172 173 174
        center_phi = phi;
        updateGL();
    }

}

Luis Peñaranda's avatar
Luis Peñaranda committed
175 176 177 178 179 180
void OpenGLCanvas::re_center(){
    center_phi=.0f;
    center_lambda=.0f;
    updateGL();
}

Luis Peñaranda's avatar
Luis Peñaranda committed
181 182 183
void OpenGLCanvas::change_fov_scale_relation(QString name){

   fov_scale_relation = name;
Luis Peñaranda's avatar
Luis Peñaranda committed
184 185
   if (fov<fov_max) scale = 1.f;
   //else if (fov>295.f) scale = 0.01f;
Luis Peñaranda's avatar
Luis Peñaranda committed
186
   else{
Luis Peñaranda's avatar
Luis Peñaranda committed
187 188 189 190 191 192 193 194 195 196 197 198
       if (fov_scale_relation == "Naive")
           scale=fov_max/fov;
       else if (fov_scale_relation == "Square Root")
           scale=sqrt((360.f-fov_max-fov)/(360.-2*fov_max));
       else if (fov_scale_relation == "Linear")
           scale=(360.f-fov_max-fov)/(360.-2*fov_max);
       else if (fov_scale_relation == "Square Power")
           scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),2);
       else if (fov_scale_relation == "Cubic Power")
           scale=powf((360.f-fov_max-fov)/(360.-2*fov_max),3);
       else if (fov_scale_relation == "Logarithm")
           scale=log(exp(1.f)+(1.f-exp(1.f))*(fov-fov_max)/(360.-2*fov_max));
Luis Peñaranda's avatar
Luis Peñaranda committed
199
   }
200
   fprintf(stderr,"changed scale relation, scale=%f, fov_max=%f\n",scale,fov_max);
Luis Peñaranda's avatar
Luis Peñaranda committed
201 202 203 204 205 206 207
   updateGL();

}

void OpenGLCanvas::change_visualization(QString name){

    visualization = name;
208
    updateGL();
Luis Peñaranda's avatar
Luis Peñaranda committed
209 210 211

}

212 213 214
// This function reads the contents of the ~/.panorc file and stores the
// options in private variables.
void OpenGLCanvas::read_config_file(){
215
#ifdef _WIN32
Luis Penaranda's avatar
Luis Penaranda committed
216 217
    char* envvar=(char*)malloc(12*sizeof(char));
    strcpy(envvar,"USERPROFILE\0");
218
    char *filepath=(char*)malloc(512*sizeof(char));
Luis Penaranda's avatar
Luis Penaranda committed
219 220 221
    strcpy(filepath,getenv(envvar));
    free(envvar);
    strcat(filepath,"\\.panorc");
222
#else
223 224 225
    struct passwd *pw=getpwuid(getuid());
    char *filepath=pw->pw_dir;
    strcat(filepath,"/.panorc");
226
#endif
227 228 229 230
    shader_dir=(char*)malloc(512*sizeof(char));
    shader_dir[0]='\0';
    input_image_file=(char*)malloc(512*sizeof(char));
    input_image_file[0]='\0';
231
    char *read_line=(char*)malloc(64*sizeof(char));
232 233
    struct stat testbuf;
    if(stat(filepath,&testbuf)){
Luis Penaranda's avatar
Luis Penaranda committed
234
        fprintf(stderr,"%s does not exist\n",filepath);
235
    }else{
Luis Penaranda's avatar
Luis Penaranda committed
236 237
        FILE *rcfile;
        FOPEN_RO(rcfile,filepath);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
        char c;
        char *line=(char*)malloc(512*sizeof(char));
        while((c=getc(rcfile))!=EOF){
            while(c=='\n') // discard empty lines
                c=getc(rcfile);
            if(c==EOF)
                break;
            line[0]=c; // first char on the line was already read
            if(!fgets(line+1,511,rcfile)){
                fprintf(stderr,"error reading rcfile\n");
                exit(-1);
            }
            // check for 'shader_dir' option
            if(!strncmp(line,"shader_dir=",11)){
                strcpy(shader_dir,line+11);
                shader_dir[strlen(line)-12]='\0';
                fprintf(stderr,"shader_dir=%s\n",shader_dir);
            }
            // check for 'image_file' option
            if(!strncmp(line,"image_file=",11)){
                strcpy(input_image_file,line+11);
                input_image_file[strlen(line)-12]='\0';
                fprintf(stderr,"input_image_file=%s\n",input_image_file);
            }
Luis Peñaranda's avatar
Luis Peñaranda committed
262 263
            // check for 'max_fov' option
            if(!strncmp(line,"max_fov=",8)){
264 265 266
                strcpy(read_line,line+8);
                read_line[strlen(line)-9]='\0';
                fov_max=atof(read_line);
Luis Peñaranda's avatar
Luis Peñaranda committed
267 268
                fprintf(stderr,"max_fov=%f\n",fov_max);
            }
269 270 271 272 273 274 275
            // check for 'auto_max_fov' option
            if(!strncmp(line,"auto_max_fov=",13)){
                strcpy(read_line,line+13);
                read_line[strlen(line)-14]='\0';
                auto_fov_max=atof(read_line);
                fprintf(stderr,"auto_max_fov=%d\n",auto_fov_max);
            }
276 277 278
        }
        fclose(rcfile);
    }
Luis Penaranda's avatar
Luis Penaranda committed
279 280 281
#ifdef _WIN32
    free(filepath);
#endif
Luis Peñaranda's avatar
Luis Peñaranda committed
282 283
    emit fov_changed((int)fov);
    emit max_fov_changed((int)fov_max);
284 285
}

Luis Penaranda's avatar
Luis Penaranda committed
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
// The file type is determined from the file extension.
enum FileType OpenGLCanvas::get_file_type(const char *filename){
    // find the last '.' in the file name
    int dotposition=-1;
    for(int i=strlen(filename)-1;i>=0;--i){
        if(filename[i]=='.'){
            dotposition=i;
            break;
        }
    }
    // if '.' was not found, the whole string is considered extension
    char *ext=(char*)filename+dotposition+1; // file extension
    if((ext[0]=='p'||ext[0]=='P')&&
       (ext[1]=='n'||ext[1]=='b'||ext[1]=='g'||ext[1]=='p'||
        ext[1]=='N'||ext[1]=='B'||ext[1]=='G'||ext[1]=='P')&&
       (ext[2]=='m'||ext[2]=='M')){
        return PNM;
    }
    if((ext[0]=='j'||ext[0]=='J')&&
       (ext[1]=='p'||ext[1]=='P')&&
       ((strlen(ext)==3&&(ext[2]=='g'||ext[2]=='G'))||
        (strlen(ext)==4&&(ext[2]=='e'||ext[2]=='E')&&
                         (ext[3]=='g'||ext[3]=='G')))){
        return JPEG;
    }
    return UNKNOWN;
}

314 315
void OpenGLCanvas::load_image(const char *new_image){
    int width,height;
316 317
#ifndef _MSC_VER
    // the progname is only used for reading the PNM format
Luis Penaranda's avatar
Luis Penaranda committed
318
    const char * const progname=(char*)(PROGNAME);
319
#endif
Luis Penaranda's avatar
Luis Penaranda committed
320 321 322 323 324 325 326 327 328
    unsigned char *textureBytes=NULL;
    int textureSize;
    switch(get_file_type(new_image)){
        case JPEG:
            fprintf(stderr,"input image has JPEG format\n");
            textureSize=jpgGetTextureSize(new_image);
            textureBytes=(unsigned char*)malloc(textureSize);
            jpgReadTextureBytes(new_image,textureBytes,&width,&height);
            break;
329
#ifndef _MSC_VER
Luis Penaranda's avatar
Luis Penaranda committed
330 331 332 333 334 335
        case PNM:
            fprintf(stderr,"input image has PNM format\n");
            textureSize=pnmGetTextureSize(progname,new_image);
            textureBytes=(unsigned char*)malloc(textureSize);
            pnmReadTextureBytes(progname,new_image,textureBytes,&width,&height);
        break;
336
#endif
Luis Penaranda's avatar
Luis Penaranda committed
337 338 339 340
        default: // UNKNOWN
            fprintf(stderr,"%s: unknown file format\n",new_image);
            exit(-1);
    }
341 342 343 344 345 346 347 348 349
    glPixelStorei(GL_UNPACK_ALIGNMENT,1);
    GLuint tex;
    glGenTextures(1,&tex);
    glBindTexture(GL_TEXTURE_2D,tex);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, width,height,0,GL_RGB,GL_UNSIGNED_BYTE,textureBytes);
}

350
void OpenGLCanvas::change_input_image(){
351
    load_image(QFileDialog::getOpenFileName(this,tr("Choose Panorama File")).toStdString().c_str());
352 353 354
    updateGL();
}

Luis Peñaranda's avatar
Luis Peñaranda committed
355 356 357 358 359 360 361 362
void OpenGLCanvas::initializeGL(){
    glShadeModel(GL_SMOOTH);
    glClearColor(1.0f,1.0f,1.0f,0.0f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);

363
#ifndef _MSC_VER
364
#ifdef __APPLE__
Luis Peñaranda's avatar
Luis Peñaranda committed
365
    const char * const progname = "PROJ_ROOT_DIR";
366 367 368 369 370
#else
    // progname is a file name or a path???
    const char * const progname = (char*)(PROGNAME);
#endif
    fprintf(stderr,"progname=%s\n",progname);
371 372
#endif // MSC_VER

373 374
    read_config_file();
    // If the input file does not exist or was not specified.
375
    struct stat testbuf;
376
    if(stat(input_image_file,&testbuf)||!strcmp(input_image_file,"")){
377 378
        load_image(QFileDialog::getOpenFileName(this,tr("Choose Panorama File")).toStdString().c_str());
    }else{
379
        load_image(input_image_file);
380
    }
381
    free(input_image_file);
Luis Peñaranda's avatar
Luis Peñaranda committed
382 383 384 385 386 387 388 389 390 391 392

    // mesh resolution
    int m,n;
    m = n = 100;

    //defining texture coordinates
    int meshNumTexCoord = m*n;
    float *texCoord = (float *)malloc(2*meshNumTexCoord*sizeof(float));
    if (texCoord == NULL){
        printf("problem allocating memory for texture coordinates \n");
    }
Luis Peñaranda's avatar
Luis Peñaranda committed
393
    define_texture_coordinates(texCoord, m, n, -CONST_PI_2_F, CONST_PI_2_F, -CONST_PI_F, CONST_PI_F);
Luis Peñaranda's avatar
Luis Peñaranda committed
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

    //defining positions of the sphere vertices
    int meshNumVertices = m*n;
    float* positions = (float *)malloc(3*meshNumVertices*sizeof(float));
    if (positions == NULL){
        printf("problem allocating memory for positions \n");
    }
//    vertex_transformation(positions, m, n, center_lambda, center_phi, fov_rads, scale); //passar pelo vertex shader
    load_sphere_mesh(positions, m, n); //colocar essa e funcoes para textura e triangulos no initializeGL

    //defining triagle indices
    unsigned int meshNumFaces = 2*(m-1)*(n-1);
    unsigned int meshNumIndices = 3*meshNumFaces;
    unsigned int * indices = (unsigned int *)malloc(meshNumIndices*sizeof(unsigned int));
    define_triangle_indices(indices, m, n);

    // draw setup
    verticesPositions = positions;
    textureCoordinates = texCoord;
    numberOfIndices = meshNumIndices;
    triangleIndices = indices;

    setShaders();
}

void OpenGLCanvas::define_texture_coordinates(float *texCoord, int m, int n, float min_phi, float max_phi, float min_lambda, float max_lambda){

    float delta_lambda = (max_lambda-min_lambda)/(1.0*(n-1));
    float delta_phi = (max_phi-min_phi)/(1.0*(m-1));

    for (int i = 0; i<m; i++){
        for (int j = 0; j<n; j++){
Luis Peñaranda's avatar
Luis Peñaranda committed
426 427
            texCoord[2*(j+i*n)] = (min_lambda+delta_lambda*j)/(2*CONST_PI_F) + 0.5;
            texCoord[2*(j+i*n)+1] = (min_phi+delta_phi*i)/(CONST_PI_F) + 0.5;
Luis Peñaranda's avatar
Luis Peñaranda committed
428 429 430 431 432 433 434
        }
    }

}

void OpenGLCanvas::vertex_transformation(float *positions, int m, int n, float center_lambda, float center_phi, float fov_rads, float scale){

Luis Peñaranda's avatar
Luis Peñaranda committed
435 436 437 438
    float min_lambda = -CONST_PI_F;
    float max_lambda = CONST_PI_F;
    float min_phi = -CONST_PI_2_F;
    float max_phi = CONST_PI_2_F;
Luis Peñaranda's avatar
Luis Peñaranda committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505

    float delta_lambda = (max_lambda-min_lambda)/(1.0*(n-1));
    float delta_phi = (max_phi-min_phi)/(1.0*(m-1));

    float lambda, phi, x, y, z, u, v, r, theta;

    //calculating the extent of the projection for the given FOV
    lambda = fov_rads;
    phi = 0.f;
    // OpenGL: x is the vertical axes pointg downwards, and y is horizontal axes
    y = sin(phi);
    x = -sin(lambda)*cos(phi);
    z = -cos(lambda)*cos(phi);
    u = 2.f*x/(-z+1.f);
    v = 2.f*y/(-z+1.f);
    r = sqrt(u*u+v*v);
    theta = atan2(u,v);
    r *= scale;
    u = -r*sin(theta);
    v = r*cos(theta);
    x = (4.f*u)/(u*u+v*v+4.f);
    y = (4.f*v)/(u*u+v*v+4.f);
    z = (u*u+v*v-4.f)/(u*u+v*v+4.f);
    u = x/(-z);
    v = y/(-z);
    float extent = u;

    for (int i = 0; i<m; i++){
        for (int j = 0; j<n; j++){

            lambda = (min_lambda+delta_lambda*j);
            phi = (min_phi+delta_phi*i);

            // OpenGL: x is the vertical axes pointg downwards, and y is horizontal axes
            y = sin(phi);
            x = -sin(lambda)*cos(phi);
            z = -cos(lambda)*cos(phi);

            //Rotation 1: (-center_lambda)-rotation on the xz-plane
            float x_copy = x;
            x = cos(-center_lambda)*x - sin(-center_lambda)*z;
            y = 1.f*y;
            z = sin(-center_lambda)*x_copy + cos(-center_lambda)*z;

            //Rotation 2: (-center_phi)-rotation on the yz-plane
            float y_copy = y;
            x = 1.f*x;
            y = cos(-center_phi)*y - sin(-center_phi)*z;
            z = sin(-center_phi)*y_copy + cos(-center_phi)*z;


            u = 2.f*x/(-z+1.f);
            v = 2.f*y/(-z+1.f);

            r = sqrt(u*u+v*v);
            theta = atan2(u,v);

            // scaling the complex plane according to scale specified in the interface (relate it to FOV)
            r *= scale;

            u = -r*sin(theta);
            v = r*cos(theta);

            x = (4.f*u)/(u*u+v*v+4.f);
            y = (4.f*v)/(u*u+v*v+4.f);
            z = (u*u+v*v-4.f)/(u*u+v*v+4.f);

Luis Peñaranda's avatar
Luis Peñaranda committed
506 507
            lambda = atan2(x,-z)/CONST_PI_F;
            phi = asin(y)/CONST_PI_2_F;
Luis Peñaranda's avatar
Luis Peñaranda committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536

            u = x/(-z);
            v = y/(-z);

            if (visualization=="Perspective"){
                positions[3*(j+i*n)] = u/extent;
                positions[3*(j+i*n)+1] = v/extent;
                positions[3*(j+i*n)+2] = z;
            }

            if (visualization=="3D Sphere"){
                positions[3*(j+i*n)] = 0.9f*x;
                positions[3*(j+i*n)+1] = 0.9f*y;
                positions[3*(j+i*n)+2] = z;
            }

            if (visualization=="Equi-Rectangular"){
                positions[3*(j+i*n)] = lambda;
                positions[3*(j+i*n)+1] = phi;
                positions[3*(j+i*n)+2] = z;
            }

        }
    }

}

void OpenGLCanvas::load_sphere_mesh(float *positions, int m, int n){

Luis Peñaranda's avatar
Luis Peñaranda committed
537 538 539 540
    float min_lambda = -CONST_PI_F;
    float max_lambda = CONST_PI_F;
    float min_phi = -CONST_PI_2_F;
    float max_phi = CONST_PI_2_F;
Luis Peñaranda's avatar
Luis Peñaranda committed
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568

    float delta_lambda = (max_lambda-min_lambda)/(1.0*(n-1));
    float delta_phi = (max_phi-min_phi)/(1.0*(m-1));

    float lambda, phi, x, y, z;

    for (int i = 0; i<m; i++){
        for (int j = 0; j<n; j++){

            lambda = (min_lambda+delta_lambda*j);
            phi = (min_phi+delta_phi*i);

            // OpenGL: x is the vertical axes pointg downwards, and y is horizontal axes
            y = sin(phi);
            x = -sin(lambda)*cos(phi);
            z = -cos(lambda)*cos(phi);

            positions[3*(j+i*n)] = x;
            positions[3*(j+i*n)+1] = y;
            positions[3*(j+i*n)+2] = z;

        }
    }

}

float OpenGLCanvas::calculate_extent(float fov_rads){

Luis Peñaranda's avatar
Luis Peñaranda committed
569
    double lambda, phi, x, y, z, u, v, r, theta;
Luis Peñaranda's avatar
Luis Peñaranda committed
570 571
    //calculating the extent of the projection for the given FOV
    lambda = fov_rads;
Luis Peñaranda's avatar
Luis Peñaranda committed
572
    phi = 0.;
Luis Peñaranda's avatar
Luis Peñaranda committed
573 574 575 576
    // OpenGL: x is the vertical axes pointg downwards, and y is horizontal axes
    y = sin(phi);
    x = -sin(lambda)*cos(phi);
    z = -cos(lambda)*cos(phi);
Luis Peñaranda's avatar
Luis Peñaranda committed
577 578
    u = 2.*x/(-z+1.);
    v = 2.*y/(-z+1.);
Luis Peñaranda's avatar
Luis Peñaranda committed
579 580 581 582 583
    r = sqrt(u*u+v*v);
    theta = atan2(u,v);
    r *= scale;
    u = -r*sin(theta);
    v = r*cos(theta);
Luis Peñaranda's avatar
Luis Peñaranda committed
584 585 586
    x = (4.*u)/(u*u+v*v+4.);
    y = (4.*v)/(u*u+v*v+4.);
    z = (u*u+v*v-4.)/(u*u+v*v+4.);
Luis Peñaranda's avatar
Luis Peñaranda committed
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
    u = x/(-z);
    v = y/(-z);
    return u;
}

void OpenGLCanvas::define_triangle_indices(unsigned int * indices, int m, int n){

    for (int i = 0; i<m-1; i++){
        for (int j = 0; j<n-1; j++){

            unsigned int index = (j+i*n);

            indices[3*(2*(j+i*(n-1)))] = index;
            indices[3*(2*(j+i*(n-1)))+1] = index+1;
            indices[3*(2*(j+i*(n-1)))+2] = index+n;

            indices[3*(2*(j+i*(n-1))+1)] = index+1;
            indices[3*(2*(j+i*(n-1))+1)+1] = index+n+1;
            indices[3*(2*(j+i*(n-1))+1)+2] = index+n;

        }
    }

}

612
#ifndef _MSC_VER
Luis Penaranda's avatar
Luis Penaranda committed
613
int OpenGLCanvas::pnmGetTextureSize(const char * const progname, const char * texturePath)
Luis Peñaranda's avatar
Luis Peñaranda committed
614 615
{
    struct pam inpam;
616
    pm_init(progname, 0);
Luis Penaranda's avatar
Luis Penaranda committed
617 618
    FILE *in_file;
    FOPEN_RO(in_file,texturePath)
619 620 621 622 623
#ifdef PAM_STRUCT_SIZE
    pnm_readpaminit(in_file,&inpam,PAM_STRUCT_SIZE(tuple_type));
#else
    pnm_readpaminit(in_file,&inpam,sizeof(struct pam));
#endif
Luis Peñaranda's avatar
Luis Peñaranda committed
624 625 626
    image_size_x=inpam.width;
    image_size_y=inpam.height;
    int size = image_size_x*image_size_y*inpam.depth*inpam.bytes_per_sample;
627 628
    pm_close(in_file);
    return size;
Luis Peñaranda's avatar
Luis Peñaranda committed
629 630
}

Luis Penaranda's avatar
Luis Penaranda committed
631
void OpenGLCanvas::pnmReadTextureBytes(const char * const progname,
Luis Peñaranda's avatar
Luis Peñaranda committed
632 633 634 635 636 637 638 639 640 641
                                          const char * texturePath,
                                          unsigned char * textureBytes,
                                          int * outImageWidth,
                                          int * outImageHeight)
{
        struct pam inpam;
        tuple * tuplerow;
        int row;

        pm_init(progname, 0);
Luis Penaranda's avatar
Luis Penaranda committed
642 643
        FILE *in_file;
        FOPEN_RO(in_file,texturePath);
Luis Peñaranda's avatar
Luis Peñaranda committed
644

645 646 647 648 649
#ifdef PAM_STRUCT_SIZE
        pnm_readpaminit(in_file,&inpam,PAM_STRUCT_SIZE(tuple_type));
#else
        pnm_readpaminit(in_file,&inpam,sizeof(struct pam));
#endif
Luis Peñaranda's avatar
Luis Peñaranda committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670

        tuplerow = pnm_allocpamrow(&inpam);

        for (row = 0; row < inpam.height; row++) {
                int column;
                pnm_readpamrow(&inpam, tuplerow);
                for (column = 0; column < inpam.width; ++column) {
                        unsigned int plane;
                        for (plane = 0; plane < inpam.depth; ++plane) {
                                textureBytes[(inpam.height-row-1)*3*inpam.width+3*column+plane] = tuplerow[column][plane];
                        }
                }
        }

        pnm_freepamrow(tuplerow);

        *outImageWidth = inpam.width;
        *outImageHeight = inpam.height;

        pm_close(in_file);
}
671
#endif
Luis Peñaranda's avatar
Luis Peñaranda committed
672

Luis Penaranda's avatar
Luis Penaranda committed
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
int OpenGLCanvas::jpgGetTextureSize(const char *texturePath)
{
    FILE *in_file;
    FOPEN_RO(in_file,texturePath)
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err=jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo,in_file);
    jpeg_read_header(&cinfo,0);
    jpeg_start_decompress(&cinfo);
    image_size_x=cinfo.output_width;
    image_size_y=cinfo.output_height;
    int size=image_size_x*image_size_y*cinfo.num_components;
    fclose(in_file);
    return size;
}

void OpenGLCanvas::jpgReadTextureBytes(const char *texturePath,
                                       unsigned char *textureBytes,
                                       int *outImageWidth,
                                       int *outImageHeight)
{
        FILE *in_file;
        FOPEN_RO(in_file,texturePath);
        struct jpeg_decompress_struct cinfo;
        struct jpeg_error_mgr jerr;
        cinfo.err=jpeg_std_error(&jerr);
        JSAMPROW row_pointer[1];
        jpeg_create_decompress(&cinfo);
        jpeg_stdio_src(&cinfo,in_file);
        jpeg_read_header(&cinfo,0);
        jpeg_start_decompress(&cinfo);
        int depth=cinfo.num_components;
        unsigned long scanline=image_size_y;
        row_pointer[0]=(unsigned char*)malloc(image_size_x*depth);
        while(cinfo.output_scanline<cinfo.output_height){
                --scanline;
                jpeg_read_scanlines(&cinfo,row_pointer,1);
                for(int i=0;i<image_size_x*depth;++i){
                    textureBytes[scanline*image_size_x*depth+i]=row_pointer[0][i];
                }
        }
        *outImageWidth=cinfo.output_width;
        *outImageHeight=cinfo.output_height;
        fclose(in_file);
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
}

Luis Peñaranda's avatar
Luis Peñaranda committed
723
void OpenGLCanvas::resizeGL(int w, int h){
724 725 726 727
    if(w>h)
        glViewport(0,(h-w)/2,w,w);
    else
        glViewport((w-h)/2,0,h,h);
Luis Peñaranda's avatar
Luis Peñaranda committed
728 729 730 731 732 733 734
}

char * OpenGLCanvas::textFileRead(char *fn) {

    FILE *fp;
    char *content = NULL;
    int f, count;
735 736
    f = OPEN_FILE(fn, O_RDONLY);
    count = LSEEK_FD(f, 0, SEEK_END);
Luis Peñaranda's avatar
Luis Peñaranda committed
737 738
//    close(f);
    if (fn != NULL) {
Luis Penaranda's avatar
Luis Penaranda committed
739 740
        //fp = fopen(fn,"rt");
        FOPEN_RO(fp,fn);
Luis Peñaranda's avatar
Luis Peñaranda committed
741 742 743 744 745 746
        if (fp != NULL) {
            if (count > 0) {
                content = (char *)malloc(sizeof(char) * (count+1));
                count = fread(content,sizeof(char),count,fp);
                content[count] = '\0';
            }
Luis Penaranda's avatar
Luis Penaranda committed
747
            fclose(fp); // maybe this line must be outside the {}
Luis Peñaranda's avatar
Luis Peñaranda committed
748 749 750 751 752 753 754 755 756
        }
    }
    return content;
}

void OpenGLCanvas::setShaders() {

    char *vs,*fs;

Luis Peñaranda's avatar
Luis Peñaranda committed
757 758
#ifndef __APPLE__
  #ifdef GLEW_VERSION_1_5
759 760 761 762 763
    GLenum err=glewInit();
    if(err!=GLEW_OK){
        fprintf(stderr,"error in GLEW initialization: %s\n",glewGetString(err));
        exit(-1);
    }
Luis Peñaranda's avatar
Luis Peñaranda committed
764
  #endif
765 766
#endif

Luis Peñaranda's avatar
Luis Peñaranda committed
767 768 769
    GLuint v = glCreateShader(GL_VERTEX_SHADER);
    GLuint f = glCreateShader(GL_FRAGMENT_SHADER);

770
    // Configure vertex and fragment shader files.
771 772
    char *vs_file=(char*)malloc(512*sizeof(char*));
    char *fs_file=(char*)malloc(512*sizeof(char*));
773 774 775 776 777 778 779 780 781 782 783 784 785 786
    if(!strcmp(shader_dir,"")){ // if shader_dir was not configured
        if(!GET_WORKDIR(vs_file,512)||!GET_WORKDIR(fs_file,512)){
            fprintf(stderr,"error reading shader files\n");
            exit(-1);
        }
        strcat(vs_file,"/shaders/");
        strcat(fs_file,"/shaders/");
    }else{;
        strcpy(vs_file,shader_dir);
        strcat(vs_file,"/");
        strcpy(fs_file,shader_dir);
        strcat(fs_file,"/");
    }
    strcat(vs_file,VERT_SHADER_FILE);
787
    strcat(fs_file,FRAG_SHADER_FILE);
788
    fprintf(stderr,"vs_file=%s\nfs_file=%s\n",vs_file,fs_file);
789 790 791 792 793 794 795 796

    struct stat vs_testbuf,fs_testbuf;
    if(stat(vs_file,&vs_testbuf)||stat(fs_file,&fs_testbuf)){
        fprintf(stderr,"a shader file does not exist!\n");
        free(vs_file);
        free(fs_file);
        exit(-1);
    }
Luis Peñaranda's avatar
Luis Peñaranda committed
797

798 799
    vs=textFileRead(vs_file);
    fs=textFileRead(fs_file);
Luis Peñaranda's avatar
Luis Peñaranda committed
800 801 802 803 804 805 806 807

    const char * vv = vs;
    const char * ff = fs;

    glShaderSource(v, 1, &vv,NULL);
    glShaderSource(f, 1, &ff,NULL);

    free(vs);free(fs);
808
    free(vs_file);free(fs_file);
Luis Peñaranda's avatar
Luis Peñaranda committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822

    glCompileShader(v);
    glCompileShader(f);

    GLuint p = glCreateProgram();

    glAttachShader(p,v);
    glAttachShader(p,f);

    glLinkProgram(p);
    glUseProgram(p);

}

Luis Peñaranda's avatar
Luis Peñaranda committed
823 824 825 826 827 828 829 830 831
void OpenGLCanvas::mousePressEvent(QMouseEvent *event){
    lastPos=event->pos();
    //fprintf(stderr,"mouse click\n");
}

void OpenGLCanvas::mouseMoveEvent(QMouseEvent *event){
    // scroll with the left button
    if(event->buttons()==Qt::LeftButton){
        // compute the delta and move the image
Luis Peñaranda's avatar
Luis Peñaranda committed
832 833
        center_lambda+=(event->x()-lastPos.x())*CONST_PI_F/image_size_x;
        center_phi+=(event->y()-lastPos.y())*CONST_PI_F/image_size_y;
Luis Peñaranda's avatar
Luis Peñaranda committed
834 835 836 837 838
        lastPos=event->pos();
        updateGL();
    }
}

839 840
void OpenGLCanvas::wheelEvent(QWheelEvent *event){
    if(event->orientation()==Qt::Vertical){
841
        if(event->modifiers()==Qt::ShiftModifier){
842 843
            change_fov_max(fov_max+((double)event->delta())/30);
        }else{
844 845 846 847 848 849 850 851 852 853 854
            int new_fov=fov+event->delta()/30;
            change_fov((double)new_fov);
            if(auto_fov_max){
                if(new_fov<60)
                    change_fov_max(60);
                else
                    if(new_fov>180)
                        change_fov_max(1);
                    else
                        change_fov_max(90-new_fov/2);
            }
855
        }
856 857 858
    }
}

Luis Peñaranda's avatar
Luis Peñaranda committed
859 860
void OpenGLCanvas::paintGL(){

Luis Peñaranda's avatar
Luis Peñaranda committed
861
    float fov_rads = (fov/360.)*CONST_PI;
Luis Peñaranda's avatar
Luis Peñaranda committed
862 863 864 865 866 867 868

//    // changing scale to generate the figures for the paper (remove it after)
//    scale = 0.8;


    // defining transformation parameters (that will be passed to the vertex shader)
    float extent = calculate_extent(fov_rads);
Luis Penaranda's avatar
Luis Penaranda committed
869
    float vis_mode=.0;
Luis Peñaranda's avatar
Luis Peñaranda committed
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
    if (visualization=="Perspective") vis_mode=1.0;
    else if (visualization=="3D Sphere") vis_mode=2.0;
    else if (visualization=="Equi-Rectangular") vis_mode=3.0;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 2.0/extent, 0.0, 2.0/scale, 0.0, -2.0/vis_mode);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glOrtho(0.0, 2.0/center_lambda, 0.0, 2.0/center_phi, -1.0, 1.0);

    // drawing the mesh
    glClearColor(1.0, 1.0, 1.0, 1.0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glColor3f(1, 0, 0);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verticesPositions);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates);

    glDrawElements(GL_TRIANGLES, numberOfIndices, GL_UNSIGNED_INT, triangleIndices);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    time_frames++;
//    if (time_frames > 0) {
        double dt = time_time.elapsed();
//        if (dt > 0.5) {
            time_fps = time_frames/dt;
            time_frames = 0;
            time_time.reset();
Luis Peñaranda's avatar
Luis Peñaranda committed
906 907
            emit fps(QString("%1 fps").arg((int)(time_fps+0.5)));
            //printf("fps = %d ", (int)(time_fps+0.5));
Luis Peñaranda's avatar
Luis Peñaranda committed
908 909 910 911
//        }
//    }

}