// 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); }