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