Newer
Older
cg / hw03 / 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 <math.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <vector>

#define PI 3.14159265358979323846
using namespace std;

static double alpha = 45.0; // rotation angle

class Vertex{
public:
    float _x, _y, _z;
    bool isNext;

    Vertex(); // empty constructor
    Vertex(float x, float y, float z); // constructor with initialization

};

void subdivChaikin(int count, int amount);
void subdivCubic(int count, int amount);

Vertex::Vertex(){}; // empty constructor

Vertex::Vertex(float x, float y, float z){ // constructor with initialization
    _x = x;
    _y = y;
    _z = z;
    isNext = false;
}

vector <Vertex> points;


void loadData()
{
    string fname = R"(C:\CLionProjects\cg\hw03\mesh1.obj)";
    ifstream file( fname);
    if (!file){
        cout << "error opening file" << endl;
        return;
    }
    string key;
    while( file){
        //getline( file, line);
        file >> key;
        if (key == "v") {
            float x, y, z;
            file >> x >> y >> z;
            Vertex pVertex = *new Vertex(x, y, 0);
            points.push_back(pVertex);
        }
    }
    file.close();

    int amount = points.size();

    subdivChaikin(3, amount);
    subdivCubic(3, amount);

}

void subdivChaikin(int count, int amount) {
    // calculate needed matrix size
    int totalcount = amount - 1;
    for (int i = 0; i < count; ++i) {
        totalcount += totalcount-2;
    }

    float P[totalcount][2];

    int i = 0;
    // copy verts to matrix
    for (auto vert : points) {
        P[i][0] = vert._x;
        P[i][1] = vert._y;

        i++;
    }

    int n = amount - 1;
    // iterate for detailed subdiv level
    for (int k = 0; k < count; ++k) {
        // init Mask
        float M[2*n-2][n];
        // M = zeros(2*n-1, n)
        for (int x = 0; x < 2 * n - 2; ++x) {
            for (int y = 0; y < n; ++y) {
                M[x][y] = 0;
            }
        }

        // set fixed values at beginning and end of Mask
        M[0][0] = 1;
        M[1][0] = .5;
        M[1][1] = .5;
        M[2*n-3][ n-1] = 1;
        M[2*n-4][ n-1] = .5;
        M[2*n-4][ n-2] = .5;

        for (int j = 2; j < n-1; ++j) {
            M[2*j-2][j-1] = .75;
            M[2*j-2][j] = .25;
            M[2*j-1][j-1] = .25;
            M[2*j-1][j] = .75;
        }

        // matrix multiplication
//        l = 2*n-2
//        m = n
//        n = 2
//        A = M
//        B = P

        // init C for temp result of matmult
        float C[2*n-2][2];
        for (int j = 0; j < 2 * n - 2; ++j) {
            C[j][0] = 0;
            C[j][1] = 0;
        }

        // actual matmult
        // C = M*P
        for (int i = 0; i < 2 * n - 2; ++i) {
            for (int k = 0; k < 2; ++k) {
                for (int j = 0; j < n; ++j) {
                    C[i][k] += M[i][j] * P[j][k];
                }
            }
        }


        // P = C
        for (int x = 0; x < 2*n-2; ++x) {
            for (int y = 0; y < 2; ++y) {
                P[x][y] = C[x][y];
            }

        }

        n = 2*n-2;

        // store new subdiv verts in vert vector
        for (i = 0; i < n; ++i) {
            Vertex pVertex = *new Vertex(P[i][0], P[i][1], ((float) k) + 2);

            // isNext means start a new strip
            if (i == 0)
                pVertex.isNext = true;

            points.push_back(pVertex);
        }
    }

}


void subdivCubic(int count, int amount) {
    // calculate needed matrix size
    int totalcount = amount - 1;
    for (int i = 0; i < count; ++i) {
        totalcount += totalcount-1;
    }

    float P[totalcount][2];

    int i = 0;
    // copy verts to matrix
    for (auto vert : points) {
        P[i][0] = vert._x;
        P[i][1] = vert._y;

        i++;
        if (i > amount)
            break;
    }

    int n = amount - 1;
    // iterate for detailed subdiv level
    for (int k = 0; k < count; ++k) {
        // init Mask
        float M[2*n-1][n];
        // M = zeros(2*n-1, n)
        for (int x = 0; x < 2 * n - 1; ++x) {
            for (int y = 0; y < n; ++y) {
                M[x][y] = 0;
            }
        }

        // set fixed values at beginning and end of Mask
        M[0][0] = 1;
        M[1][0] = 3.0f/8.0f;
        M[1][1] = 3.0f/4.0f;
        M[1][2] = -1.0f/8.0f;
        M[2*n-4][ n-2] = 1;
        M[2*n-3][ n-1] = 3.0f/8.0f;
        M[2*n-3][n-2] = 3.0f/4.0f;
        M[2*n-3][n-3] = -1.0f/8.0f;
        M[2*n-2][ n-1] = 1;

        for (int j = 2; j < n - 1; ++j) {
            M[2*j-2][j-1] = 1;
            M[2*j-1][j-2] = -1.0f/16.0f;
            M[2*j-1][j-1] = 9.0f/16.0f;
            M[2*j-1][j] = 9.0f/16.0f;
            M[2*j-1][j+1] = -1.0f/16.0f;
        }


        // matrix multiplication
//        l = 2*n-2
//        m = n
//        n = 2
//        A = M
//        B = P

        // init C for temp result of matmult
        float C[2*n-1][2];
        for (int j = 0; j < 2 * n - 1; ++j) {
            C[j][0] = 0;
            C[j][1] = 0;
        }

        // actual matmult
        // C = M*P
        for (int i = 0; i < 2 * n - 1; ++i) {
            for (int k = 0; k < 2; ++k) {
                for (int j = 0; j < n; ++j) {
                    C[i][k] += M[i][j] * P[j][k];
                }
            }
        }


        // P = C
        for (int x = 0; x < 2*n-1; ++x) {
            for (int y = 0; y < 2; ++y) {
                P[x][y] = C[x][y];
            }
        }

        n = 2*n-1;

        // store new subdiv verts in vert vector
        for (i = 0; i < n; ++i) {
            Vertex pVertex = *new Vertex(P[i][0], P[i][1] - 1.0f, -((float)k) - 2.0f);

            // isNext means start a new strip
            if (i == 0)
                pVertex.isNext = true;

            points.push_back(pVertex);
        }
    }

}


void DrawTriag() {

    glLineWidth(1.0f);
    glBegin( GL_LINE_STRIP);
    for (auto vert : points) {
        if (vert.isNext) {
            glEnd();
            glLineWidth(2*abs(vert._z));
            glBegin( GL_LINE_STRIP);
        }
        glVertex3f(vert._x, vert._y, vert._z);
    }
    glEnd();
}

// 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

    loadData();
}

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 ,0 ,-10.0);     // Move 10 units backwards in z, since camera is at origin
    glScaled( 2.0, 2.0, 2.0);       // 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, 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);
}