diff -r 000000000000 -r 1918ee327afb examples/opengl/shared/qtlogo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/opengl/shared/qtlogo.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,412 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include + +#include "qtlogo.h" + +static const qreal tee_height = 0.311126; +static const qreal cross_width = 0.25; +static const qreal bar_thickness = 0.113137; +static const qreal inside_diam = 0.20; +static const qreal outside_diam = 0.30; +static const qreal logo_depth = 0.10; +static const int num_divisions = 32; + +//! [0] +struct Geometry +{ + QVector faces; + QVector vertices; + QVector normals; + void appendSmooth(const QVector3D &a, const QVector3D &n, int from); + void appendFaceted(const QVector3D &a, const QVector3D &n); + void finalize(); + void loadArrays() const; +}; +//! [0] + +//! [1] +class Patch +{ +public: + enum Smoothing { Faceted, Smooth }; + Patch(Geometry *); + void setSmoothing(Smoothing s) { sm = s; } + void translate(const QVector3D &t); + void rotate(qreal deg, QVector3D axis); + void draw() const; + void addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n); + void addQuad(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &d); + + GLushort start; + GLushort count; + GLushort initv; + + GLfloat faceColor[4]; + QMatrix4x4 mat; + Smoothing sm; + Geometry *geom; +}; +//! [1] + +static inline void qSetColor(float colorVec[], QColor c) +{ + colorVec[0] = c.redF(); + colorVec[1] = c.greenF(); + colorVec[2] = c.blueF(); + colorVec[3] = c.alphaF(); +} + +void Geometry::loadArrays() const +{ + glVertexPointer(3, GL_FLOAT, 0, vertices.constData()); + glNormalPointer(GL_FLOAT, 0, normals.constData()); +} + +void Geometry::finalize() +{ + // TODO: add vertex buffer uploading here + + // Finish smoothing normals by ensuring accumulated normals are returned + // to length 1.0. + for (int i = 0; i < normals.count(); ++i) + normals[i].normalize(); +} + +void Geometry::appendSmooth(const QVector3D &a, const QVector3D &n, int from) +{ + // Smooth normals are acheived by averaging the normals for faces meeting + // at a point. First find the point in geometry already generated + // (working backwards, since most often the points shared are between faces + // recently added). + int v = vertices.count() - 1; + for ( ; v >= from; --v) + if (qFuzzyCompare(vertices[v], a)) + break; + if (v < from) + { + // The vert was not found so add it as a new one, and initialize + // its corresponding normal + v = vertices.count(); + vertices.append(a); + normals.append(n); + } + else + { + // Vert found, accumulate normals into corresponding normal slot. + // Must call finalize once finished accumulating normals + normals[v] += n; + } + // In both cases (found or not) reference the vert via its index + faces.append(v); +} + +void Geometry::appendFaceted(const QVector3D &a, const QVector3D &n) +{ + // Faceted normals are achieved by duplicating the vert for every + // normal, so that faces meeting at a vert get a sharp edge. + int v = vertices.count(); + vertices.append(a); + normals.append(n); + faces.append(v); +} + +Patch::Patch(Geometry *g) + : start(g->faces.count()) + , count(0) + , initv(g->vertices.count()) + , sm(Patch::Smooth) + , geom(g) +{ + qSetColor(faceColor, QColor(Qt::darkGray)); +} + +void Patch::rotate(qreal deg, QVector3D axis) +{ + mat.rotate(deg, axis); +} + +void Patch::translate(const QVector3D &t) +{ + mat.translate(t); +} + +static inline void qMultMatrix(const QMatrix4x4 &mat) +{ + if (sizeof(qreal) == sizeof(GLfloat)) + glMultMatrixf((GLfloat*)mat.constData()); +#ifndef QT_OPENGL_ES + else if (sizeof(qreal) == sizeof(GLdouble)) + glMultMatrixd((GLdouble*)mat.constData()); +#endif + else + { + GLfloat fmat[16]; + qreal const *r = mat.constData(); + for (int i = 0; i < 16; ++i) + fmat[i] = r[i]; + glMultMatrixf(fmat); + } +} + +//! [2] +void Patch::draw() const +{ + glPushMatrix(); + qMultMatrix(mat); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, faceColor); + + const GLushort *indices = geom->faces.constData(); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, indices + start); + glPopMatrix(); +} +//! [2] + +void Patch::addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n) +{ + QVector3D norm = n.isNull() ? QVector3D::normal(a, b, c) : n; + if (sm == Smooth) + { + geom->appendSmooth(a, norm, initv); + geom->appendSmooth(b, norm, initv); + geom->appendSmooth(c, norm, initv); + } + else + { + geom->appendFaceted(a, norm); + geom->appendFaceted(b, norm); + geom->appendFaceted(c, norm); + } + count += 3; +} + +void Patch::addQuad(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &d) +{ + QVector3D norm = QVector3D::normal(a, b, c); + if (sm == Smooth) + { + addTri(a, b, c, norm); + addTri(a, c, d, norm); + } + else + { + // If faceted share the two common verts + addTri(a, b, c, norm); + int k = geom->vertices.count(); + geom->appendSmooth(a, norm, k); + geom->appendSmooth(c, norm, k); + geom->appendFaceted(d, norm); + count += 3; + } +} + +static inline QVector extrude(const QVector &verts, qreal depth) +{ + QVector extr = verts; + for (int v = 0; v < extr.count(); ++v) + extr[v].setZ(extr[v].z() - depth); + return extr; +} + +class Rectoid +{ +public: + void translate(const QVector3D &t) + { + for (int i = 0; i < parts.count(); ++i) + parts[i]->translate(t); + } + void rotate(qreal deg, QVector3D axis) + { + for (int i = 0; i < parts.count(); ++i) + parts[i]->rotate(deg, axis); + } + + // No special Rectoid destructor - the parts are fetched out of this member + // variable, and destroyed by the new owner + QList parts; +}; + +class RectPrism : public Rectoid +{ +public: + RectPrism(Geometry *g, qreal width, qreal height, qreal depth); +}; + +RectPrism::RectPrism(Geometry *g, qreal width, qreal height, qreal depth) +{ + enum { bl, br, tr, tl }; + Patch *fb = new Patch(g); + fb->setSmoothing(Patch::Faceted); + + // front face + QVector r(4); + r[br].setX(width); + r[tr].setX(width); + r[tr].setY(height); + r[tl].setY(height); + QVector3D adjToCenter(-width / 2.0, -height / 2.0, depth / 2.0); + for (int i = 0; i < 4; ++i) + r[i] += adjToCenter; + fb->addQuad(r[bl], r[br], r[tr], r[tl]); + + // back face + QVector s = extrude(r, depth); + fb->addQuad(s[tl], s[tr], s[br], s[bl]); + + // side faces + Patch *sides = new Patch(g); + sides->setSmoothing(Patch::Faceted); + sides->addQuad(s[bl], s[br], r[br], r[bl]); + sides->addQuad(s[br], s[tr], r[tr], r[br]); + sides->addQuad(s[tr], s[tl], r[tl], r[tr]); + sides->addQuad(s[tl], s[bl], r[bl], r[tl]); + + parts << fb << sides; +} + +class RectTorus : public Rectoid +{ +public: + RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int numSectors); +}; + +RectTorus::RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int k) +{ + QVector inside; + QVector outside; + for (int i = 0; i < k; ++i) { + qreal angle = (i * 2 * M_PI) / k; + inside << QVector3D(iRad * qSin(angle), iRad * qCos(angle), depth / 2.0); + outside << QVector3D(oRad * qSin(angle), oRad * qCos(angle), depth / 2.0); + } + inside << QVector3D(0.0, iRad, 0.0); + outside << QVector3D(0.0, oRad, 0.0); + QVector in_back = extrude(inside, depth); + QVector out_back = extrude(outside, depth); + + // Create front, back and sides as seperate patches so that smooth normals + // are generated for the curving sides, but a faceted edge is created between + // sides and front/back + Patch *front = new Patch(g); + for (int i = 0; i < k; ++i) + front->addQuad(outside[i], inside[i], + inside[(i + 1) % k], outside[(i + 1) % k]); + Patch *back = new Patch(g); + for (int i = 0; i < k; ++i) + back->addQuad(in_back[i], out_back[i], + out_back[(i + 1) % k], in_back[(i + 1) % k]); + Patch *is = new Patch(g); + for (int i = 0; i < k; ++i) + is->addQuad(in_back[i], in_back[(i + 1) % k], + inside[(i + 1) % k], inside[i]); + Patch *os = new Patch(g); + for (int i = 0; i < k; ++i) + os->addQuad(out_back[(i + 1) % k], out_back[i], + outside[i], outside[(i + 1) % k]); + parts << front << back << is << os; +} + +QtLogo::QtLogo(QObject *parent, int divisions, qreal scale) + : QObject(parent) + , geom(new Geometry()) +{ + buildGeometry(divisions, scale); +} + +QtLogo::~QtLogo() +{ + qDeleteAll(parts); + delete geom; +} + +void QtLogo::setColor(QColor c) +{ + for (int i = 0; i < parts.count(); ++i) + qSetColor(parts[i]->faceColor, c); +} + +//! [3] +void QtLogo::buildGeometry(int divisions, qreal scale) +{ + qreal cw = cross_width * scale; + qreal bt = bar_thickness * scale; + qreal ld = logo_depth * scale; + qreal th = tee_height *scale; + + RectPrism cross(geom, cw, bt, ld); + RectPrism stem(geom, bt, th, ld); + + QVector3D z(0.0, 0.0, 1.0); + cross.rotate(45.0, z); + stem.rotate(45.0, z); + + qreal stem_downshift = (th + bt) / 2.0; + stem.translate(QVector3D(0.0, -stem_downshift, 0.0)); + + RectTorus body(geom, 0.20, 0.30, 0.1, divisions); + + parts << stem.parts << cross.parts << body.parts; + + geom->finalize(); +} +//! [3] + +//! [4] +void QtLogo::draw() const +{ + geom->loadArrays(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + for (int i = 0; i < parts.count(); ++i) + parts[i]->draw(); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); +} +//! [4]