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

//
// Created by Pascal on 17.05.2021.
//

#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include "Mesh.h"
#include "Util.h"

using namespace std;

Mesh::Mesh() {
    this->tris = {};
    this->pts = {};
}


void Mesh::saveData(const string& fileName) {
    ofstream file( fileName);
    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();
}

bool Mesh::loadData(const string& fileName) {
    ifstream file( fileName);
    if (!file){
        cout << "error opening file" << endl;
        return false;
    }
    string key;
    while( file){
        //getline( file, line);
        file >> key;
        if (key == "v") {
            // vertex
            float x, y, z;
            file >> x >> y >> z;
            Vertex pVertex = *new Vertex(this, x, y, z);
            pts.push_back(pVertex);
        } else if (key == "f") {
            // face (only triangles supported)
            int a, b, c;
            file >> a >> b >> c;
            Tri pTriangle = *new Tri(this, a-1, b-1, c-1);
            tris.push_back(pTriangle);
        }
    }
    file.close();

    tris.pop_back();

    return true;
}

void Mesh::connectivityAlgo() {
    for (auto & pt : pts) {
        pt.valence = 0;
    }

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

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

        tris[i] = triag;
    }
}

void Mesh::subDivLoop(int count) {
    for (int i = 0; i < count; ++i) {
        this->subDivLoop();
    }
}

void Mesh::subDivLoop() {
    this->connectivityAlgo();

    for (int i = 0; i < tris.size(); ++i) {
        Tri triag = tris[i];
        Vertex a = pts[triag.iv[0]];
        Vertex b = pts[triag.iv[1]];
        Vertex c = pts[triag.iv[2]];

        for (int tI = 0; tI < 3; ++tI) {
            Tri t = tris[triag.it[tI]];

            // figure out, which neighbour I am to my neighbour
            int otherEI = 0;
            for (int j = 0; j < 3; ++j) {
                if (t.it[j] == i) {
                    otherEI = j;
                    break;
                }
            }
            if (i < triag.it[tI]) {
                Vertex d = pts[t.iv[otherEI]];
                Vertex e = (1.0f / 8.0f) *
                           ((((tI == 1 || tI == 2) ? 3.0f : 1.0f) * a) + (((tI == 0 || tI == 2) ? 3.0f : 1.0f) * b) +
                            (((tI == 1 || tI == 0) ? 3.0f : 1.0f) * c) + d);
                triag.ie[tI] = pts.size();
                pts.push_back(e);
            } else {
                // the calculated edge-mask is already present in the neighbour

                triag.ie[tI] = t.ie[otherEI];
            }
        }

        tris[i] = triag;
    }

    for (auto &pt : pts) { // multiply every vertex with beta
        int n = pt.valence; // n = valence of v_i
        if (n < 3) continue;
        float beta = Util::beta_n(n);
        pt *= beta; // v_i *= beta(n)
    }

    // remember the original length, because it will grow
    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 e[3] = {};

        for (int tI = 0; tI < 3; ++tI) {
            e[tI] = pts[triag.ie[tI]];
        }

//        cout << triag.ie[0] << " " << triag.ie[1] << " " << triag.ie[2] << endl;
//        cout << e[0].p[0] << " " << e[0].p[1] << " " << e[0].p[2] << endl;
//        cout << e[1].p[0] << " " << e[1].p[1] << " " << e[1].p[2] << endl;
//        cout << e[2].p[0] << " " << e[2].p[1] << " " << e[2].p[2] << endl << endl;


        a += (0.5f * ((1-Util::beta_n(a.valence)) / float(a.valence)) * (e[1] + e[2]));
        b += (0.5f * ((1-Util::beta_n(b.valence)) / float(b.valence)) * (e[0] + e[2]));
        c += (0.5f * ((1-Util::beta_n(c.valence)) / float(c.valence)) * (e[1] + e[0]));


        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;

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

void Mesh::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]];

        // calculate the midpoints of all edges
        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);

        // check if any of the midpoints if already a known vertex
        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);
        }
        if (ei1 == -1) {
            ei1 = pts.size();
            pts.push_back(e1);
        }
        if (ei2 == -1) {
            ei2 = pts.size();
            pts.push_back(e2);
        }

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


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

Mesh* Mesh::copy() {
    Mesh *copy = new Mesh();
    copy->drawWireframe = this->drawWireframe;
    copy->drawOutline = this->drawOutline;

    for (auto vert : this->pts) {
        copy->pts.push_back(*vert.copy(copy));
    }
    for (auto tri : this->tris) {
        copy->tris.push_back(*tri.copy(copy));
    }


    return copy;
}