2013年3月2日 星期六

3D text on Qt with OpenGL (part 2)


這一篇,是把上一篇的文章改成一個class




header file
#include <QString>
#include <QFont>
#include <QVector3D>
#include <QVector>

class Mesh{
public:
    QVector<QVector3D> points;
    QVector<QVector3D> normals;
    QVector<int> lengths;
    QVector<unsigned int> types;
};

class Text3D
{
public:
    Text3D(QString text, QFont font, qreal size = 0.5f);
    Mesh getEdge(){return edge;}
    Mesh getEdge3D(){return edge3D;}
    Mesh get2D(){return front;}
    Mesh get3D(){return all;}

private:
    void extrude(QPointF prePoint, QPointF point, qreal z);
    Mesh face(QList<QPolygonF> polygons, qreal z);
    void merge(Mesh mesh);
    Mesh edge;
    Mesh edge3D;
    Mesh front;
    Mesh all;
};



source file

#include <QPainterPath>
#include <GL/glu.h>
#ifndef CALLBACK
#define CALLBACK
#endif

Text3D::Text3D(QString text, QFont font, qreal size)
{
    size = size*0.5;

    QPainterPath path;
    path.addText(QPointF(0, 0), font, text);
    QRectF rect = path.boundingRect();
    QPointF center = rect.center();
    // move to center;
    QList<QPolygonF> polygons;
    foreach(QPolygonF path, path.toSubpathPolygons()){
        QPolygonF polygon;
        foreach(QPointF point, path){
            polygon << point - center;
        }
        polygons << polygon;
    }

    // edge
    foreach(QPolygonF polygon, polygons){
        foreach(QPointF point, polygon){
            edge.points << QVector3D(point);
            edge.normals << QVector3D(0,0,1);
        }
        edge.lengths << polygon.count();
        edge.types << GL_LINE_LOOP;
    }

    // 3d edge
    foreach(QPolygonF polygon, polygons){
        QPointF startPoint,prePoint;
        foreach(QPointF point, polygon){
            if(startPoint.isNull()){
                startPoint = point;
            }else{
                extrude(prePoint, point, size);
            }
            prePoint = point;
        }
        extrude(prePoint, startPoint, size);

        edge3D.lengths << polygon.count()*6;
        edge3D.types << GL_TRIANGLES;
    }

    //front
    front = face(polygons, size);

    //back
    QList<QPolygonF> invertPolygons;
    foreach(QPolygonF path, polygons){
        QPolygonF polygon;
        for(int i = path.count()-1; i>=0; i--){
            polygon << path.at(i);
        }
        invertPolygons << polygon;
    }
    Mesh back = face(invertPolygons, -size);

    merge(front);
    merge(back);
    merge(edge3D);
}

void Text3D::extrude(QPointF prePoint, QPointF point, qreal z)
{
    qreal x1 = prePoint.rx();
    qreal y1 = prePoint.ry();
    qreal x2 = point.rx();
    qreal y2 = point.ry();

    edge3D.points << QVector3D(x1, y1, +z);
    edge3D.points << QVector3D(x2, y2, +z);
    edge3D.points << QVector3D(x1, y1, -z);

    edge3D.points << QVector3D(x2, y2, -z);
    edge3D.points << QVector3D(x1, y1, -z);
    edge3D.points << QVector3D(x2, y2, +z);

    QVector3D n = QVector3D::normal
        (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));

    edge3D.normals << n;
    edge3D.normals << n;
    edge3D.normals << n;

    edge3D.normals << n;
    edge3D.normals << n;
    edge3D.normals << n;
}

QVector<QVector3D> tessPoints;
QVector<QVector3D> tessNormals;
QVector<int> tessLengths;
QVector<unsigned int> tessTypes;
int counter;

void CALLBACK tessBeginCB(GLenum which)
{
    tessTypes << which;
    counter=0;
}

void CALLBACK tessEndCB()
{
    tessLengths << counter;
}

void CALLBACK tessVertexCB(const GLvoid *data)
{
    const GLdouble *ptr = (const GLdouble*)data;
    tessPoints << QVector3D(ptr[0], ptr[1], ptr[2]);
    tessNormals << QVector3D(0,0,1);
    counter++;
}

#include <QDebug>
void CALLBACK errorCB(GLenum errorCode){
    const GLubyte *estring;
    estring = gluErrorString(errorCode);
    qDebug()<<estring;
}

Mesh Text3D::face(QList<QPolygonF> polygons, qreal z)
{
    tessPoints.clear();
    tessNormals.clear();
    tessLengths.clear();
    tessTypes.clear();

    GLUtesselator *tess = gluNewTess();
    gluTessCallback(tess, GLU_TESS_BEGIN, (void (__stdcall *)())tessBeginCB);
    gluTessCallback(tess, GLU_TESS_END, (void (__stdcall *)())tessEndCB);
    gluTessCallback(tess, GLU_TESS_VERTEX, (void (__stdcall *)())tessVertexCB);
    gluTessCallback(tess, GLU_TESS_ERROR,  (void (__stdcall *)())errorCB);
    gluTessBeginPolygon(tess, NULL);
        foreach(QPolygonF polygon, polygons){
            GLdouble (*quad)[3] = new GLdouble[polygon.count()][3];
            for(int i=0; i<polygon.count(); i++){
                QPointF point = polygon.at(i);
                quad[i][0] = point.rx();
                quad[i][1] = point.ry();
                quad[i][2] =  z;
            }
            gluTessBeginContour(tess);
            for(int i=0; i<polygon.count(); i++){
                gluTessVertex(tess, quad[i], quad[i]);
            }
            gluTessEndContour(tess);
        }
    gluTessEndPolygon(tess);
    gluDeleteTess(tess);
    Mesh mesh;
    mesh.points = tessPoints;
    mesh.normals = tessNormals;
    mesh.lengths = tessLengths;
    mesh.types = tessTypes;

    return mesh;
}

void Text3D::merge(Mesh mesh)
{
    all.points << mesh.points;
    all.normals << mesh.normals;
    all.lengths << mesh.lengths;
    all.types << mesh.types;
}

沒有留言: