//--------------------------------------------------
// Panoramic is an interface for the visualization of panoramas capable of
// handling wide fields of view, based on Möbius transformations.
// Copyright (C) 2015 Luis Peñaranda, Luiz Velho and Leonardo Sacht.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see .
//--------------------------------------------------
#include
#ifdef NEEDGLEE
#include "GLee.h"
#endif
#include "openglcanvas.h"
#include
#include // for fstat, getpwuid, getuid
#include // for fstat
#ifdef _WIN32
#include // for getenv (TODO: use getenv_s)
#else
#include // for getpwuid
#endif
#include // for getc
#include
#include "files.h"
#define VERT_SHADER_FILE "vertex_shader.vert"
#define FRAG_SHADER_FILE "fragment_shader.frag"
#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)
#ifdef PANO_EXTENDED_CONTROLS
// These definitions specify which attributes should be used to store some
// parameters passed to the vertex shader. Nvidia hardware
// only leaves attributes 1 and 7 unreserved; attributes 8 to 15 are
// reserved for textures.
// TODO: we use attributes 14 and 15, which work for Nvidia; we need to
// test with other hardware.
#define PD_ATTR 13
#define ZBL_ATTR 14
#define ZBR_ATTR 15
#endif // PANO_EXTENDED_CONTROLS
OpenGLCanvas::OpenGLCanvas(QWidget *parent) :
QGLWidget(parent)
{
setFormat(QGL::DoubleBuffer | QGL::DepthBuffer);
fov = 60.f;
fov_max = 60.f; // TODO: I only use floats here because Leo did... check
scale = 1.0f;
center_lambda = 0.f;
center_phi = 0.f;
fov_scale_relation = "Simple";
visualization = "Moebius";
auto_fov_max=false;
shrink_for_all=false;
#ifdef PANO_EXTENDED_CONTROLS
pd=0.f; // Pannini projection d
zblambda=.1f; // Zorin-Barr transformation lambda
zbR=1.f; // Zorin-Barr transformation R
#endif // PANO_EXTENDED_CONTROLS
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){
if (fov!=f && f>=1.f && f<=360.f)
fov=f;
compute_scale();
// scale = 0.3f;
fprintf(stderr,"change fov, fov=%f, fov_max=%f, scale=%f\n",fov,fov_max,scale);
emit fov_changed((int)fov);
updateGL();
}
void OpenGLCanvas::automaxbutton(bool autovalue){
auto_fov_max=autovalue;
change_fov_max(compute_auto_fov_max(fov));
}
void OpenGLCanvas::shrinkallbutton(bool shrinkvalue){
shrink_for_all=shrinkvalue;
compute_scale();
updateGL();
}
void OpenGLCanvas::change_fov(int new_fov){
if(new_fov<=360&&new_fov>=1){
change_fov((double)new_fov);
if(auto_fov_max)
change_fov_max(compute_auto_fov_max(new_fov));
}
}
void OpenGLCanvas::change_fov_max(int new_fov_max){
if(new_fov_max<=360.f&&new_fov_max>=1)
fov_max=(double)new_fov_max;
compute_scale();
fprintf(stderr,"change fov_max, fov=%f, fov_max=%f, new scale=%f\n",fov,fov_max,scale);
emit max_fov_changed((int)fov_max);
updateGL();
}
#ifdef PANO_EXTENDED_CONTROLS
void OpenGLCanvas::change_p_d(int new_p_d){
pd=(float)new_p_d/10;
fprintf(stderr,"p_d=%f\n",pd);
glVertexAttrib1f(PD_ATTR,pd);
updateGL();
}
void OpenGLCanvas::change_zb_lambda(int new_zb_lambda){
zblambda=(float)new_zb_lambda/1000;
fprintf(stderr,"zb_lambda=%f\n",zblambda);
glVertexAttrib1f(ZBL_ATTR,zblambda);
updateGL();
}
void OpenGLCanvas::change_zb_R(int new_zb_R){
zbR=(float)new_zb_R/100;
fprintf(stderr,"zb_R=%f\n",zbR);
glVertexAttrib1f(ZBR_ATTR,zbR);
updateGL();
}
#endif // PANO_EXTENDED_CONTROLS
//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){
if (center_lambda!=lambda && lambda>=-CONST_PI_F && lambda<=CONST_PI_F) {
center_lambda = lambda;
updateGL();
}
}
void OpenGLCanvas::change_center_phi(double phi){
if (center_phi!=phi && phi>=-CONST_PI_2_F && phi<=CONST_PI_2_F) {
center_phi = phi;
updateGL();
}
}
void OpenGLCanvas::re_center(){
center_phi=.0f;
center_lambda=.0f;
updateGL();
}
void OpenGLCanvas::change_fov_scale_relation(QString name){
fov_scale_relation = name;
compute_scale();
fprintf(stderr,"changed scale relation, scale=%f, fov_max=%f\n",scale,fov_max);
updateGL();
}
void OpenGLCanvas::change_visualization(QString name){
visualization=name;
compute_scale();
updateGL();
}
// This function reads the contents of the ~/.panorc file and stores the
// options in private variables.
void OpenGLCanvas::read_config_file(){
#ifdef _WIN32
char* envvar=(char*)malloc(12*sizeof(char));
strcpy(envvar,"USERPROFILE\0");
char *filepath=(char*)malloc(512*sizeof(char));
strcpy(filepath,getenv(envvar));
free(envvar);
strcat(filepath,"\\.panorc");
#else
struct passwd *pw=getpwuid(getuid());
char *filepath=pw->pw_dir;
strcat(filepath,"/.panorc");
#endif
shader_dir=(char*)malloc(512*sizeof(char));
shader_dir[0]='\0';
input_image_file=(char*)malloc(512*sizeof(char));
input_image_file[0]='\0';
input_image_dir=(char*)malloc(512*sizeof(char));
input_image_dir[0]='\0';
char *read_line=(char*)malloc(64*sizeof(char));
struct stat testbuf;
if(stat(filepath,&testbuf)){
fprintf(stderr,"%s does not exist\n",filepath);
}else{
FILE *rcfile;
FOPEN_RO(rcfile,filepath);
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);
}
// check for 'image_dir' option
if(!strncmp(line,"image_dir=",10)){
strcpy(input_image_dir,line+10);
input_image_dir[strlen(line)-11]='\0';
fprintf(stderr,"input_image_dir=%s\n",input_image_dir);
}
// check for 'max_fov' option
if(!strncmp(line,"max_fov=",8)){
strcpy(read_line,line+8);
read_line[strlen(line)-9]='\0';
fov_max=atof(read_line);
fprintf(stderr,"max_fov=%f\n",fov_max);
}
// check for 'fov' option
if(!strncmp(line,"fov=",4)){
strcpy(read_line,line+4);
read_line[strlen(line)-5]='\0';
fov=atof(read_line);
fprintf(stderr,"fov=%f\n",fov);
}
// 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);
}
#ifdef PANO_EXTENDED_CONTROLS
// check for the Pannini parameter, d
if(!strncmp(line,"pd=",3)){
strcpy(read_line,line+3);
read_line[strlen(line)-4]='\0';
pd=atof(read_line);
fprintf(stderr,"pd=%f\n",pd);
}
// check for the Zorin-Barr transformation parameters, lambda and R
if(!strncmp(line,"zblambda=",9)){
strcpy(read_line,line+9);
read_line[strlen(line)-10]='\0';
zblambda=atof(read_line);
fprintf(stderr,"zblambda=%f\n",zblambda);
}
if(!strncmp(line,"zbR=",4)){
strcpy(read_line,line+4);
read_line[strlen(line)-5]='\0';
zbR=atof(read_line);
fprintf(stderr,"zbR=%f\n",zbR);
}
#endif // PANO_EXTENDED_CONTROLS
}
fclose(rcfile);
}
#ifdef _WIN32
free(filepath);
#endif
emit fov_changed((int)fov);
emit max_fov_changed((int)fov_max);
}
void OpenGLCanvas::load_image(const char *new_image){
unsigned char *textureBytes=read_image(new_image,&image_size_x,&image_size_y);
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,
image_size_x,
image_size_y,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
textureBytes);
}
void OpenGLCanvas::change_input_image(){
const char *fname=QFileDialog::getOpenFileName(this,tr("Choose Panorama File"),input_image_dir,FileRead::image_types).toStdString().c_str();
if(strlen(fname)>0){
load_image(fname);
updateGL();
}
}
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);
#ifndef _MSC_VER
#ifdef __APPLE__
const char * const progname = "PROJ_ROOT_DIR";
#else
// progname is a file name or a path???
const char * const progname = (char*)(PROGNAME);
#endif
fprintf(stderr,"progname=%s\n",progname);
#endif // MSC_VER
read_config_file();
compute_scale();
// If the input file does not exist or was not specified.
struct stat testbuf;
if(stat(input_image_file,&testbuf)||!strcmp(input_image_file,"")){
load_image(QFileDialog::getOpenFileName(this,tr("Choose Panorama File"),input_image_dir,FileRead::image_types).toStdString().c_str());
}else{
load_image(input_image_file);
}
free(input_image_file);
// 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");
}
define_texture_coordinates(texCoord, m, n, -CONST_PI_2_F, CONST_PI_2_F, -CONST_PI_F, CONST_PI_F);
//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; i179)
return 1;
//if(new_fov>60)
return (90-new_fov/2);
}
// This function computes the scale, in function of the projection method,
// the fov/scale ratio and the value of shrink_for_all.
void OpenGLCanvas::compute_scale(){
if (shrink_for_all || visualization=="Moebius" || visualization=="3D Sphere"){
if (fov<=fov_max){
scale=1.f;
//else if (fov>295.f)
// scale = 0.02f; // TODO: check this value wrt fov_max
}else{
if(fov_scale_relation == "Simple")
scale=fov_max/fov;
else if(fov_scale_relation == "Square Root")
scale=sqrtf((360.f-fov_max-fov)/(360.f-2.f*fov_max));
else if(fov_scale_relation == "Linear")
scale=(360.f-fov_max-fov)/(360.f-2.f*fov_max);
else if(fov_scale_relation == "Square Power")
scale=powf((360.f-fov_max-fov)/(360.f-2.f*fov_max),2.f);
else if(fov_scale_relation == "Cubic Power")
scale=powf((360.f-fov_max-fov)/(360.f-2.f*fov_max),3.f);
else if(fov_scale_relation == "Logarithm")
scale=logf(expf(1.f)+(1.f-expf(1.f))*(fov-fov_max)/(360.f-2.f*fov_max));
}
}else{
scale=1.f;
}
}
void OpenGLCanvas::resizeGL(int w, int h){
if(w>h)
glViewport(0,(h-w)/2,w,w);
else
glViewport((w-h)/2,0,h,h);
}
char * OpenGLCanvas::textFileRead(char *fn) {
FILE *fp;
char *content = NULL;
int f, count;
f = OPEN_FILE(fn, O_RDONLY);
count = LSEEK_FD(f, 0, SEEK_END);
// close(f);
if (fn != NULL) {
FOPEN_RO(fp,fn);
if (fp != NULL) {
if (count > 0) {
content = (char *)malloc(sizeof(char) * (count+1));
count = fread(content,sizeof(char),count,fp);
content[count] = '\0';
}
fclose(fp); // maybe this line must be outside the {}
}
}
return content;
}
void OpenGLCanvas::setShaders() {
char *vs,*fs;
#ifndef __APPLE__
#ifdef GLEW_VERSION_1_5
GLenum err=glewInit();
if(err!=GLEW_OK){
fprintf(stderr,"error in GLEW initialization: %s\n",glewGetString(err));
exit(-1);
}
#endif
#endif
GLuint v = glCreateShader(GL_VERTEX_SHADER);
GLuint f = glCreateShader(GL_FRAGMENT_SHADER);
// Configure vertex and fragment shader files.
char *vs_file=(char*)malloc(512*sizeof(char*));
char *fs_file=(char*)malloc(512*sizeof(char*));
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);
strcat(fs_file,FRAG_SHADER_FILE);
fprintf(stderr,"vs_file=%s\nfs_file=%s\n",vs_file,fs_file);
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);
}
vs=textFileRead(vs_file);
fs=textFileRead(fs_file);
const char * vv = vs;
const char * ff = fs;
glShaderSource(v, 1, &vv,NULL);
glShaderSource(f, 1, &ff,NULL);
free(vs);free(fs);
free(vs_file);free(fs_file);
glCompileShader(v);
glCompileShader(f);
GLuint p = glCreateProgram();
#ifdef PANO_EXTENDED_CONTROLS
// Bind attributes pd, zblambda and zbR to the vertex shader.
glVertexAttrib1f(PD_ATTR,pd);
glBindAttribLocation(p,PD_ATTR,"pd");
glVertexAttrib1f(ZBL_ATTR,zblambda);
glBindAttribLocation(p,ZBL_ATTR,"zblambda");
glVertexAttrib1f(ZBR_ATTR,zbR);
glBindAttribLocation(p,ZBR_ATTR,"zbR");
#endif // PANO_EXTENDED_CONTROLS
glAttachShader(p,v);
glAttachShader(p,f);
glLinkProgram(p);
glUseProgram(p);
}
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
center_lambda+=(event->x()-lastPos.x())*CONST_PI_F/image_size_x;
center_phi+=(event->y()-lastPos.y())*CONST_PI_F/image_size_y;
lastPos=event->pos();
updateGL();
}
}
void OpenGLCanvas::wheelEvent(QWheelEvent *event){
if(event->orientation()==Qt::Vertical){
if(event->modifiers()==Qt::ShiftModifier){
change_fov_max(fov_max+((double)event->delta())/30);
}else{
int new_fov=fov+event->delta()/30;
change_fov((double)new_fov);
if(auto_fov_max)
change_fov_max(compute_auto_fov_max(new_fov));
}
}
}
void OpenGLCanvas::paintGL(){
float fov_rads = (fov/360.f)*CONST_PI_F;
// // 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);
float vis_mode=.0f;
if (visualization=="Moebius" || visualization=="Perspective") vis_mode=1.f;
else if (visualization=="3D Sphere") vis_mode=2.f;
else if (visualization=="Equi-Rectangular") vis_mode=3.f;
else if (visualization=="Stereographic") vis_mode=4.f;
else if (visualization=="Mercator") vis_mode=5.f;
else if (visualization=="Orthographic") vis_mode=4.5f;
#ifdef PANO_EXTENDED_CONTROLS
else if (visualization=="Pannini") vis_mode=4.25f;
else if (visualization=="Zorin-Barr") vis_mode=6.f;
#endif // PANO_EXTENDED_CONTROLS
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();
emit fps(QString("%1 fps").arg((int)(time_fps+0.5)));
//printf("fps = %d ", (int)(time_fps+0.5));
// }
// }
}