Newer
Older
cg / hw04 / documentation / code / oglwidget.cpp
// Copyright (c) 2021. Pascal Syma <pascal@syma.dev> and Antonio Martinez Casadesus <acasadesus@stud.hs-bremen.de>.
// All rights reserved.

#include "oglwidget.h"
#include "src/Mesh.h"
#include <string>
#include <vector>

#define PI 3.14159265358979323846
using namespace std;

static double alpha = 45.0; // rotation angle

/// Read-only mesh
Mesh *originalMesh;
/// Outer mesh
Mesh *parentMesh;
/// Inner mesh
Mesh *childMesh;

bool init = false;
bool childWireframe = false;
bool drawParent = false;
int parentSubdivCount = 0;
int childSubdivCount = 1;
float scale = 2.0f;

/// Draw a mesh.
/// \param mesh Mesh to draw
void drawMesh(Mesh mesh) {
    glPolygonMode(GL_FRONT_AND_BACK, mesh.drawWireframe ? GL_LINE : GL_FILL);
    if (mesh.drawWireframe)
        glDisable(GL_CULL_FACE);
    else
        glEnable(GL_CULL_FACE);

    glBegin(GL_TRIANGLES);
    for (auto tri : mesh.tris) {
        Vertex a = mesh.pts[tri.iv[0]];
        Vertex b = mesh.pts[tri.iv[1]];
        Vertex c = mesh.pts[tri.iv[2]];
        Vertex nvec = ((b - a) % (c - a));

        glNormal3fv(nvec.p);

        glVertex3fv(a.p);
        glVertex3fv(b.p);
        glVertex3fv(c.p);

    }
    glEnd();
    if (!mesh.drawOutline) return;

    // draw the edges on top of the faces
    for (auto tri : mesh.tris) {
        glLineWidth(3);
        glBegin( GL_LINE_STRIP);
        Vertex a = mesh.pts[tri.iv[0]];
        Vertex b = mesh.pts[tri.iv[1]];
        Vertex c = mesh.pts[tri.iv[2]];

        glVertex3fv(a.p);
        glVertex3fv(b.p);
        glVertex3fv(c.p);

        glEnd();
    }
}

/// Draw the two meshes
void DrawTriag() {
    if (!init) return;

    drawMesh(*childMesh);
    if (drawParent) drawMesh(*parentMesh);

}

/// initialize Open GL lighting and projection matrix
void InitLightingAndProjection() // to be executed once before drawing
{
    // light positions and colors
    GLfloat LightPosition1[4] = { 10, 5, 10,  0};
    GLfloat LightPosition2[4] = { -5, 5, -10,  0};
    GLfloat ColorRedish[4] = { 1.0,  .8,  .8,  1}; // white with a little bit of red
    GLfloat ColorBlueish[4] = { .8,  .8,  1.0,  1};// white with a little bit of blue

    glEnable( GL_DEPTH_TEST); // switch on z-buffer
    glDepthFunc( GL_LESS);

    glShadeModel( GL_SMOOTH); // Gouraud shading
    //glShadeModel( GL_FLAT);

    glEnable( GL_LIGHTING); // use lighting
    glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, 1); // draw both sides

    // define and switch on light 0
    glLightfv( GL_LIGHT0, GL_POSITION, LightPosition1);
    glLightfv( GL_LIGHT0, GL_DIFFUSE,  ColorRedish);
    glLightfv( GL_LIGHT0, GL_SPECULAR, ColorRedish);
    glEnable( GL_LIGHT0);

    // define and switch on light 1
    glLightfv( GL_LIGHT1, GL_POSITION, LightPosition2);
    glLightfv( GL_LIGHT1, GL_DIFFUSE,  ColorBlueish);
    glLightfv( GL_LIGHT1, GL_SPECULAR, ColorBlueish);
    glEnable( GL_LIGHT1);

    glMatrixMode( GL_PROJECTION); // define camera projection
    glLoadIdentity(); // reset matrix to identity (otherwise existing matrix will be multiplied with)
    glOrtho( -15, 15, -10, 10, -50, 50); // orthogonal projection (xmin xmax ymin ymax zmin zmax)
    //glFrustum( -10, 10, -8, 8, 2, 20); // perspective projektion
}

/// define material color properties for front and back side
void SetMaterialColor( int side, float r, float g, float b){
    float	amb[4], dif[4], spe[4];
    int	i, mat;

    dif[0] = r; // diffuse color as defined by r,g, and b
    dif[1] = g;
    dif[2] = b;
    for( i=0; i<3; i++){
        amb[i] = .1 * dif[i]; // ambient color is 10 percent of diffuse
        spe[i] = .5; // specular color is just white / gray
    }
    amb[3] = dif[3] = spe[3] = 1.0; // alpha component is always 1
    switch( side){
        case 1:	mat = GL_FRONT; break;
        case 2:	mat = GL_BACK; break;
        default: mat = GL_FRONT_AND_BACK; break;
    }
    glMaterialfv( mat, GL_AMBIENT, amb); // define ambient, diffuse and specular components
    glMaterialfv( mat, GL_DIFFUSE, dif);
    glMaterialfv( mat, GL_SPECULAR, spe);
    glMaterialf( mat, GL_SHININESS, 50.0); // Phong constant for the size of highlights
}


OGLWidget::OGLWidget(QWidget *parent) // constructor
    : QOpenGLWidget(parent)
{
    // Setup the animation timer to fire every x msec
    animtimer = new QTimer(this);
    animtimer->start( 50 );

    // Everytime the timer fires, the animation is going one step forward
    connect(animtimer, SIGNAL(timeout()), this, SLOT(stepAnimation()));

    animstep = 0;
}

OGLWidget::~OGLWidget() // destructor
{
}

void OGLWidget::stepAnimation()
{
    animstep++;    // Increase animation steps
    update();      // Trigger redraw of scene with paintGL
}

void OGLWidget::initializeGL() // initializations to be called once
{
    initializeOpenGLFunctions();

    InitLightingAndProjection(); // define light sources and projection

}

void OGLWidget::paintGL() // draw everything, to be called repeatedly
{
    glEnable(GL_NORMALIZE); // this is necessary when using glScale (keep normals to unit length)

    // set background color
    glClearColor(0.8, 0.8, 1.0, 1.0); // bright blue
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // draw the scene
    glMatrixMode( GL_MODELVIEW);
    glLoadIdentity();				// Reset The Current Modelview Matrix
    glTranslated( 0 ,-5 ,-10.0);     // Move 10 units backwards in z, since camera is at origin
    glScaled( scale, scale, scale);       // scale objects
    glRotated( alpha, 0, 3, 1);     // continuous rotation
    alpha += 2;

    // define color: 1=front, 2=back, 3=both, followed by r, g, and b
    SetMaterialColor( 1, 1.0, .2, .2);  // front color is red
    SetMaterialColor( 2, 0.2, 0.2, 1.0); // back color is blue

    // draw a cylinder with default resolution
    DrawTriag();

    // make it appear (before this, it's hidden in the rear buffer)
    glFlush();
}

void OGLWidget::resizeGL(int w, int h) // called when window size is changed
{
    // adjust viewport transform
    glViewport(0,0,w,h);
}

/// Clone and subdivide the outer mesh.
/// Callback from UI.
/// \param i Subdivision steps
void OGLWidget::parentSubdiv(int i) {
    parentSubdivCount = i;

    parentMesh = originalMesh->copy();
    parentMesh->drawWireframe = true;
    parentMesh->subDivLoop(parentSubdivCount);
}

/// Clone and subdivide the inner mesh.
/// Callback from UI.
/// \param i Subdivision steps
void OGLWidget::childSubdiv(int i) {
    childSubdivCount = i;

    childMesh = originalMesh->copy();
    childMesh->drawWireframe = childWireframe;
    childMesh->subDivLoop(childSubdivCount);
}

/// Whether or not to draw the outer mesh.
/// Callback from UI.
/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked)
void OGLWidget::drawOutline(int i) {
    drawParent = i == 2;
}

/// Whether or not to draw the inner mesh.
/// Callback from UI.
/// \param i Selection state of the checkbox (0=Unchecked; 1=PartiallyChecked; 2=Checked)
void OGLWidget::drawWireframe(int i) {
    childWireframe = i == 2;
    childMesh->drawWireframe = childWireframe;
}

/// Set global scale.
/// Callback from UI.
/// \param i Slider value
void OGLWidget::setScale(int i) {

    scale = float(i) / 10.0f;
}

/// Load a mesh from filepath.
/// Callback from UI.
/// \see bool Mesh::loadData(const string& fileName)
/// \param filename Path to mesh file
/// \return True, if loading was successful
bool OGLWidget::loadFile(string filename) {

    originalMesh = new Mesh();
    bool success = originalMesh->loadData(filename);

    init = success;
    if (!success) return false;


    OGLWidget::childSubdiv(childSubdivCount);

//    childMesh->saveData( R"(C:\CLionProjects\cg\hw04\test2.obj)");

    OGLWidget::parentSubdiv(parentSubdivCount);

    return true;
}