Newer
Older
cg / hw04 / oglwidget.cpp
@Pascal Syma Pascal Syma on 10 May 2021 14 KB added hw05
// 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 p[3]; // coordinates

    Vertex();
    Vertex( float point[3]);
    Vertex( float x, float y, float z);
    void Print() {
        cout << p[0] << " " << p[1] << " " << p[2] << endl;
    }
    void operator*=( float a);
    void operator+=( Vertex a);
    // print coordinates on screen

};

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

Vertex::Vertex(float x, float y, float z){ // constructor with initialization
    p[0] = x;
    p[1] = y;
    p[2] = z;
}

Vertex::Vertex(float *point) {
    copy_n(point, 3, p);
}

Vertex operator+( Vertex a, Vertex b) // add pts or vectors
{
    return *new Vertex(a.p[0] + b.p[0], a.p[1] + b.p[1],a.p[2] + b.p[2]);
}
Vertex operator-( Vertex a, Vertex b)// subtract pts or vectors
{
    return *new Vertex(a.p[0] - b.p[0], a.p[1] - b.p[1],a.p[2] - b.p[2]);
}
Vertex operator*( float a, Vertex b) // product between scalar and vector
{
    return *new Vertex(a*b.p[0], a*b.p[1], a*b.p[2]);
}
float operator*( Vertex a, Vertex b) // scalar product
{
    return a.p[0] * b.p[0] + a.p[1] * b.p[1] + a.p[2] * b.p[2];
}
Vertex operator%( Vertex a, Vertex b) // cross product
{
    return *new Vertex(a.p[1]*b.p[2] - a.p[2]*b.p[1], a.p[2]*b.p[0] - a.p[0]*b.p[2], a.p[0]*b.p[1] - a.p[1]*b.p[0]);
}
bool operator==(Vertex a, Vertex b) // check if coords are the same
{
    return (a.p[0] == b.p[0]) && (a.p[1] == b.p[1]) && (a.p[2] == b.p[2]);
}
void Vertex::operator*=( float a)
{
    this->p[0] *= a;
    this->p[1] *= a;
    this->p[2] *= a;
}
void Vertex::operator+=( Vertex a)
{
    this->p[0] += a.p[0];
    this->p[1] += a.p[1];
    this->p[2] += a.p[2];
}

class Tri
{
public:
    int iv[3]; // vertex indices
    int it[3]; // adjacent triangle indices
    int ie[3]; // edge vertex indices
    Tri();
    Tri( int i[3]);
    Tri( int i, int j, int k);
    void Print();
};

float beta_n( int n);
void subDivEdgeMidpoint();

void saveData();

void subDivLoop();

Tri::Tri() {
    it[0] = -1;
    it[1] = -1;
    it[2] = -1;
    ie[0] = -1;
    ie[1] = -1;
    ie[2] = -1;
}

Tri::Tri(int *i) {
    new (this) Tri();

}

Tri::Tri(int i, int j, int k) {
    new (this) Tri();

    iv[0] = i;
    iv[1] = j;
    iv[2] = k;
}


vector <Vertex> pts;
vector <int> valences;  // valence list (no. of triangles for every point
vector <Tri> tris;

void Tri::Print() {
    cout << "Tri:" << endl << " neighbour: " << it[0] << " " << it[1] << " " << it[2] << endl << " - ";
    cout << valences[iv[0]] << "x ";
    pts[iv[0]].Print();
    cout << " - " << valences[iv[1]] << "x ";
    pts[iv[1]].Print();
    cout << " - " << valences[iv[2]] << "x ";
    pts[iv[2]].Print();
    cout << endl;
}

float beta_n( int n)
{
    float an = (3.0f/8.0f) + pow((3.0f/8.0f) + (1.0f/4.0f)*cos(2.0f*PI/n), 2);

    return (8.0f/5.0f)*an - (3.0f/5.0f);
}


void loadData()
{
    string fname = R"(C:\CLionProjects\cg\hw04\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, z);
            pts.push_back(pVertex);
            valences.push_back(0);
        } else if (key == "f") {
            int a, b, c;
            file >> a >> b >> c;
            Tri pTriangle = *new Tri(a-1, b-1, c-1);
            tris.push_back(pTriangle);
        }
    }
    file.close();

    tris.pop_back();

//    subDivEdgeMidpoint();
//    subDivEdgeMidpoint();

    // Connectivity Algorithm
    for (int i = 0; i < tris.size(); ++i) {
        Tri triag = tris[i];
        int n = 0;
        // Search for neighbour
        for (int ti = 0; ti < tris.size(); ++ti) {
            if (i == ti) continue;
            Tri t = tris[ti];
            int count = 0;
            for (int x : triag.iv) {
                for (int y : t.iv) {
                    if (x == y)
                        count++;
                }
            }
            // if two points are the same, they are neighbours
            if (count == 2)
                triag.it[n++] = ti;
        }

        // increase valence for each vertex of triangle
        valences[triag.iv[0]]++;
        valences[triag.iv[1]]++;
        valences[triag.iv[2]]++;

        tris[i] = triag;
    }


    for (auto triag : tris) {
        triag.Print();
    }
    subDivLoop();


    saveData();
}

void subDivLoop() {

    for( int i=0; i< pts.size(); i++){ // multiply every vertex with beta
        break;
        int n = valences[i]; // n = valence of v_i
        float beta = beta_n( n);
        pts[i] *= beta; // v_i *= beta(n)
    }

    const unsigned long long int amountToSubDiv = tris.size();
    for (int i = 0; i < amountToSubDiv; ++i) {
        Tri triag = tris[i];

        int ai = triag.iv[0];
        Vertex a = pts[ai];
        int bi = triag.iv[1];
        Vertex b = pts[bi];
        int ci = triag.iv[2];
        Vertex c = pts[ci];

        Vertex d;
        for (int ti : triag.it) {
            int di = -1;
            Tri t = tris[ti];
            int count = 0;
            for (int y : t.iv) {
                if (ci == y || bi == y)
                    count++;
                else
                    di = y;
            }
            if (count == 2) {
                cout << " 1: " << di;
                d = pts[di];
                break;
            }
        }

        Vertex e0 = (1.0f/8.0f)*(a + (3.0f*b) + (3.0f*c) + d);
        triag.ie[0] = pts.size();
        pts.push_back(e0);
        valences.push_back(0);



        for (auto ti: triag.it) {
            int di = -1;
            Tri t = tris[ti];
            int count = 0;
            for (int y : t.iv) {
                if (ai == y || ci == y)
                    count++;
                else
                    di = y;
            }
            if (count == 2) {
                cout << " 2: " << di;
                d = pts[di];
                break;
            }
        }

        Vertex e1 = (1.0f/8.0f)*((3.0f*a) + b + (3.0f*c) + d);
        triag.ie[1] = pts.size();
        pts.push_back(e1);
        valences.push_back(0);


        for (auto ti: triag.it) {
            int di = -1;
            Tri t = tris[ti];
            int count = 0;
            for (int y : t.iv) {
                if (ai == y || bi == y)
                    count++;
                else
                    di = y;
            }
            if (count == 2) {
                cout << " 3: " << di << endl;
                d = pts[di];
                break;
            }
        }

        Vertex e2 = (1.0f/8.0f)*((3.0f*a) + (3.0f*b) + c + d);
        triag.ie[2] = pts.size();
        pts.push_back(e2);
        valences.push_back(0);


        cout << e0.p[0] << " " << e0.p[1] << " " << e0.p[2] << endl;
        cout << e1.p[0] << " " << e1.p[1] << " " << e1.p[2] << endl;
        cout << e2.p[0] << " " << e2.p[1] << " " << e2.p[2] << endl << endl;


//        a += ((1-beta_n(valences[ai])) / (valences[ai])) * (0.5f * (e1 + e2));
//        b += ((1-beta_n(valences[bi])) / (valences[bi])) * (0.5f * (e0 + e2));
//        c += ((1-beta_n(valences[ci])) / (valences[ci])) * (0.5f * (e1 + e0));


//        pts[ai] = a;
//        pts[bi] = b;
//        pts[ci] = c;


        triag.iv[0] = triag.ie[1];
        triag.iv[1] = triag.ie[0];
        triag.iv[2] = ci;

        tris[i] = triag;
//        continue;

        tris.push_back(*new Tri(triag.ie[1], triag.ie[2], triag.ie[0]));
        tris.push_back(*new Tri(ai, triag.ie[2], triag.ie[1]));
        tris.push_back(*new Tri(triag.ie[2], bi, triag.ie[0]));
    }

}

void saveData() {
    string fname = R"(C:\CLionProjects\cg\hw04\test2.obj)";
    ofstream file( fname);
    if (!file){
        cout << "error opening file" << endl;
        return;
    }

    for (auto vert : pts) {
        file << "v\t" << vert.p[0] << "\t" << vert.p[1] << "\t" << vert.p[2] << endl;
    }

    for (auto triag : tris) {
        file << "f\t" << triag.iv[0]+1 << "\t" << triag.iv[1]+1 << "\t" << triag.iv[2]+1 << endl;
    }

    file.close();
}


void subDivEdgeMidpoint() {
    const unsigned long long int amountToSubDiv = tris.size();
    for (int i = 0; i < amountToSubDiv; ++i) {
        Tri triag = tris[i];

        Vertex a = pts[triag.iv[0]];
        Vertex b = pts[triag.iv[1]];
        Vertex c = pts[triag.iv[2]];

//        int e = pts.size();
        int ei0 = -1;
        int ei1 = -1;
        int ei2 = -1;
        Vertex e0 = 0.5f * (a+b);
        Vertex e1 = 0.5f * (c+b);
        Vertex e2 = 0.5f * (a+c);
        for (int j = 0; j < pts.size(); ++j) {
            if (pts[j] == e0)
                ei0 = j;
            if (pts[j] == e1)
                ei1 = j;
            if (pts[j] == e2)
                ei2 = j;
        }
        if (ei0 == -1) {
            ei0 = pts.size();
            pts.push_back(e0);
            valences.push_back(0);
        }
        if (ei1 == -1) {
            ei1 = pts.size();
            pts.push_back(e1);
            valences.push_back(0);
        }
        if (ei2 == -1) {
            ei2 = pts.size();
            pts.push_back(e2);
            valences.push_back(0);
        }

        tris.push_back(*new Tri(triag.iv[2], ei2, ei1));
        tris.push_back(*new Tri(triag.iv[1], ei0, ei1));
        tris.push_back(*new Tri(ei0, ei1, ei2));


        triag.iv[1] = ei0;
        triag.iv[2] = ei2;
        tris[i] = triag;
    }
}

void DrawTriag() {

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

        glNormal3fv(nvec.p);

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

    }
    glEnd();

    glBegin(GL_POINTS);
    for (auto vert : pts) {
        glVertex3fv(vert.p);
    }
    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);
}