Subversion Repositories gelsvn

Rev

Rev 195 | Rev 337 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

// ----------------------------------------
// A simple OBJ viewer.
//
// Controls:
// - left mouse down + mouse motion : rotate
// - Scroll button and +- buttons   : zoom
// - right mouse click              : centre trackball
// - esc                            : exits
// - x,y,z buttons                  : switch trackball up axis
// ----------------------------------------

#if (_MSC_VER >= 1200)
#pragma warning (disable: 4786)
#endif

#include <list>
#include <vector>

#include <assert.h>
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#endif
#include <string.h>
#include <stdlib.h>

#include <iostream>
#include <CGLA/Vec2i.h>
#include <CGLA/Vec2f.h>
#include <IL/il.h>
#include <IL/ilu.h>

#include <CGLA/Vec3f.h>
#include <CGLA/Mat4x4f.h>
#include "GLGraphics/gel_glut.h"
#include "GLGraphics/SimpleTrackBall.h"
#include "GLGraphics/draw.h"
#include "Geometry/TriMesh.h"
#include "Geometry/obj_load.h"

using namespace std;
using namespace CGLA;
using namespace Geometry;
using namespace GLGraphics;

namespace
{
int win_size_x = 800;
int win_size_y = 800;

SimpleTrackBall* ball;

int main_window;

bool load_image_into_texture(const std::string& name, GLuint& id)
{
    unsigned char* image = 0;
    unsigned int bpp = 0;
    unsigned int size_x = 0;
    unsigned int size_y = 0;
    unsigned int load_size_x = 0;
    unsigned int load_size_y = 0;


    ILenum Error;
    ILuint  ImgId;

    static bool washere = false;
    if(!washere)
    {
        ilInit();
        iluInit();
        washere=true;
    }
    ilEnable(IL_CONV_PAL);
    ilEnable(IL_ORIGIN_SET);
    ilOriginFunc(IL_ORIGIN_LOWER_LEFT);
    ilGenImages(1, &ImgId);
    ilBindImage(ImgId);
    char* name_cstr = const_cast<char*>(name.c_str());
    if(!ilLoadImage(name_cstr))
    {
        cout << "could not load <" << name_cstr  << ">" << endl;
        return false;
    }

    load_size_x = ilGetInteger(IL_IMAGE_WIDTH);
    load_size_y = ilGetInteger(IL_IMAGE_HEIGHT);
    bpp = ilGetInteger(IL_IMAGE_BITS_PER_PIXEL);

    if (bpp==24)
        ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
    else if (bpp==32)
        ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
    else if (bpp==8)
        ilConvertImage(IL_LUMINANCE, IL_UNSIGNED_BYTE);
    else
        assert(0);

    unsigned int i;
    for (i=2;i<=4096 ;i*=2)
        if (i>=load_size_x)
            break;
    size_x = i;
    for (i=2;i<=4096 ;i*=2)
        if (i>=load_size_y)
            break;
    size_y = i;

    if(size_x != load_size_x || size_y != load_size_y)
    {
        iluImageParameter(ILU_FILTER, ILU_BILINEAR);
        iluScale(size_x, size_y, 1);
    }

    const int image_size =size_x*size_y*ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);
    image = new unsigned char[image_size];
    memcpy(image, ilGetData(), image_size);
    ilDeleteImages(1, &ImgId);

    bool any_errors=false;
    while ((Error = ilGetError())) {
        cout << __LINE__ << "Error: " << iluErrorString(Error) << endl;
        any_errors = true;
    }
    if(any_errors) return false;

    GLint internalFormat=0;
    GLenum format=0;

    glGenTextures(1, &id);
    switch (bpp)
    {
    case 8:
        internalFormat =  GL_LUMINANCE;
        format = GL_LUMINANCE;
        break;
    case 24:
        internalFormat =  GL_RGB;
        format = GL_RGB;
        break;
    case 32:
        internalFormat =  GL_RGBA;
        format = GL_RGBA;
        break;
    default:
        return false;
    }

    glBindTexture(GL_TEXTURE_2D, id);
    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size_x, size_y, 0,
                 format, GL_UNSIGNED_BYTE, image);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    return true;
}

void enable_textures(TriMesh& tm)
{
    for(unsigned int i=0;i<tm.materials.size(); ++i)
    {
        Material& mat = tm.materials[i];
        if(mat.tex_name != "")
        {
            string name = mat.tex_path + mat.tex_name;

            GLuint tex_id;
            if(load_image_into_texture(name, tex_id))
                                                        mat.tex_id = tex_id;
                                }
    }
}

bool depth_pick(int x, int y,Vec3f& wp)
{
    // Enquire about the viewport dimensions
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);

    // Get the minimum and maximum depth values.
    float minmax_depth[2];
    glGetFloatv(GL_DEPTH_RANGE, minmax_depth);

    // Read a single pixel at the position of the mouse cursor.
    float depth;
    glReadPixels(x, viewport[3]-y, 1,1, GL_DEPTH_COMPONENT,
                 GL_FLOAT, (void*) &depth);

    // If the depth corresponds to the far plane, we clicked on the
    // background.
    if(depth == minmax_depth[1])
        return false;

    // The lines below copy the viewing transformation from OpenGL
    // to local variables. The call to gluLookAt must have exactly
    // the same parameters as when the scene is drawn.
    glLoadIdentity();
    ball->gl_view();
    double mvmat[16];
    glGetDoublev(GL_MODELVIEW_MATRIX, mvmat);

    // Copy the projection matrix. We assume it is unchanged.
    double prjmat[16];
    glGetDoublev(GL_PROJECTION_MATRIX, prjmat);

    // Now unproject the point from screen to world coordinates.
    double ox, oy, oz;
    gluUnProject(x,viewport[3]-y,depth,
                 mvmat,prjmat,viewport,
                 &ox, &oy, &oz);

    wp = Vec3f(ox,oy,oz);

    return true;
}


TriMesh mesh;

void mouse_motion(int x, int y)
{
    ball->roll(x,y);
}

void mouse(int button, int state, int x, int y)
{
    if(button==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)
    {
        Vec3f p;
        if(depth_pick(x, y, p))
            ball->set_center(p);
    }
    else if(button==3)
        ball->farther();
    else if(button==4)
        ball->closer();
}

void idle()
{
    if ( glutGetWindow() != main_window )
        glutSetWindow(main_window);
    glutPostRedisplay();
}

void display()
{
    static bool washere = false;
    static unsigned int l;
    if(!washere)
    {
        cout << "Creating display list" << endl;
        l = glGenLists(1);
        glNewList(l, GL_COMPILE);
        draw(mesh);
        glEndList();
        washere = true;
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    ball->gl_view();
    glCallList(l);
    glutSwapBuffers();
}

void keyboard(unsigned char key, int x, int y)
{
    switch(key)
    {
    case '\033': exit(0); break;
    case '+': ball->closer(); break;
    case '-': ball->farther(); break;
    default:
        ball->up_axis(key);
    }
}
}

int main(int argc, char** argv)
{
    // GLUT INIT
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH);
    glutInitWindowSize(win_size_x, win_size_y);
    glutInit(&argc, argv);
    main_window = glutCreateWindow("OBJ Viewer");
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);
    glutMotionFunc(mouse_motion);
    glutMouseFunc(mouse);
    glutIdleFunc(idle);

    // GL INIT
    glClearColor(.8f, 0.9f, 1.0f, 0.f);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
    glShadeModel(GL_SMOOTH);

    // LOAD OBJ
    string fn;
    if(argc>1)
        fn = argv[1];
    else
        fn = "../../data/head.obj";

    cout << "Loading " << fn << endl;
    if(fn == "") exit(0);

    obj_load(fn, mesh);
    enable_textures(mesh);
    if(!mesh.has_normals())
    {
        cout << "Computing normals" << endl;
        mesh.compute_normals();
    }

    // Initialize Trackball
    Vec3f c;
    float r;
    mesh.get_bsphere(c,r);
    r *= 1.5;
    ball = new SimpleTrackBall(c,r);

    // Setup projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(53,1.0f,r/100,r*3);
    glMatrixMode(GL_MODELVIEW);

    // Pass control to GLUT
    glutMainLoop();

    return 0;
}