summaryrefslogtreecommitdiffstats
path: root/src/threed/geometry
diff options
context:
space:
mode:
Diffstat (limited to 'src/threed/geometry')
-rw-r--r--src/threed/geometry/geometry.pri28
-rw-r--r--src/threed/geometry/qgeometrydata.cpp2025
-rw-r--r--src/threed/geometry/qgeometrydata.h213
-rw-r--r--src/threed/geometry/qglbezierpatches.cpp815
-rw-r--r--src/threed/geometry/qglbezierpatches.h97
-rw-r--r--src/threed/geometry/qglbuilder.cpp1378
-rw-r--r--src/threed/geometry/qglbuilder.h140
-rw-r--r--src/threed/geometry/qglbuilder_p.h89
-rw-r--r--src/threed/geometry/qglcube.cpp168
-rw-r--r--src/threed/geometry/qglcube.h76
-rw-r--r--src/threed/geometry/qglcylinder.cpp384
-rw-r--r--src/threed/geometry/qglcylinder.h105
-rw-r--r--src/threed/geometry/qgldome.cpp256
-rw-r--r--src/threed/geometry/qgldome.h83
-rw-r--r--src/threed/geometry/qglmaterialcollection.cpp415
-rw-r--r--src/threed/geometry/qglmaterialcollection.h101
-rw-r--r--src/threed/geometry/qglsection.cpp696
-rw-r--r--src/threed/geometry/qglsection_p.h131
-rw-r--r--src/threed/geometry/qglsphere.cpp243
-rw-r--r--src/threed/geometry/qglsphere.h79
-rw-r--r--src/threed/geometry/qglteapot.cpp104
-rw-r--r--src/threed/geometry/qglteapot.h64
-rw-r--r--src/threed/geometry/qglteapot_data_p.h408
-rw-r--r--src/threed/geometry/qlogicalvertex.cpp416
-rw-r--r--src/threed/geometry/qlogicalvertex.h328
-rw-r--r--src/threed/geometry/qvector_utils_p.h110
26 files changed, 8952 insertions, 0 deletions
diff --git a/src/threed/geometry/geometry.pri b/src/threed/geometry/geometry.pri
new file mode 100644
index 000000000..5623ac054
--- /dev/null
+++ b/src/threed/geometry/geometry.pri
@@ -0,0 +1,28 @@
+INCLUDEPATH += $$PWD
+VPATH += $$PWD
+HEADERS += qglcube.h \
+ qglsphere.h \
+ qgeometrydata.h \
+ qlogicalvertex.h \
+ qglbuilder.h \
+ qglbezierpatches.h \
+ qglmaterialcollection.h \
+ qglteapot.h \
+ qglcylinder.h \
+ qgldome.h
+SOURCES += qglcube.cpp \
+ qglsphere.cpp \
+ qgeometrydata.cpp \
+ qglbuilder.cpp \
+ qglsection.cpp \
+ qglbezierpatches.cpp \
+ qglmaterialcollection.cpp \
+ qglteapot.cpp \
+ qlogicalvertex.cpp \
+ qglcylinder.cpp \
+ qgldome.cpp
+PRIVATE_HEADERS += qglteapot_data_p.h \
+ qglbuilder_p.h \
+ qglsection_p.h \
+ qglteapot_data_p.h \
+ qvector_utils_p.h
diff --git a/src/threed/geometry/qgeometrydata.cpp b/src/threed/geometry/qgeometrydata.cpp
new file mode 100644
index 000000000..f5cedc960
--- /dev/null
+++ b/src/threed/geometry/qgeometrydata.cpp
@@ -0,0 +1,2025 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qgeometrydata.h"
+#include "qlogicalvertex.h"
+#include "qglpainter.h"
+
+#include <QtOpenGL/qgl.h>
+#include <QtCore/qdebug.h>
+
+/*!
+ \class QGeometryData
+ \brief The QGeometryData class encapsulates sets of geometry data.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The QGeometryData class encloses a number of data arrays
+ that model most typical vertex data needs. The class provides a
+ store for all of the data types in the QGL::VertexAttribute enumeration.
+
+ \table
+ \header
+ \o QGL::VertexAttribute
+ \o QGeometryData functions
+ \row
+ \o QGL::Position
+ \o appendVertex(), vertex(), vertices()
+ \row
+ \o QGL::Normal
+ \o appendNormal(), normal(), normals()
+ \row
+ \o QGL::Color
+ \o appendColor(), colorRef(), colors()
+ \row
+ \o QGL::TextureCoord0 - QGL::TextureCoord3
+ \o appendTexCoord(), texCoordRef(), texCoords()
+ \row
+ \o QGL::CustomVertex0 - QGL::CustomVertex1, QGL::UserVertex
+ \o appendAttribute(), vector3DAttribute(), attributes()
+ \endtable
+
+ Additionally the class provides the following features:
+ \list
+ \o appendVertex() for adding a QLogicalVertex()
+ \o logicalVertexAt() for return the data at an index as a QLogicalVertex()
+ \o hasField() to find if a particular data type is present
+ \o normalizeNormals() to reduce all normal vectors to unit length
+ \o boundingBox() to find the bounds of the geometry
+ \endlist
+
+ It is up to the user of a QGeometryData instance to ensure that the
+ data has an equal number of items in each field. For example, if five
+ vertices are added and only two normals are added, the logical vertex at
+ position 3 will be corrupt, since it does not have a normal.
+
+ While data is being accumulated the counts of different fields will vary,
+ since it may be convenient to add several vertices, then several normals,
+ colors or attributes at a time. However when a logical vertex is
+ constructed or when the data is sent to the GPU, counts of all fields
+ must be equal.
+
+ QGeometryData uses explicit sharing with lazy creation of internal
+ data so that code like:
+ \code
+ QGeometryData myData;
+ if (processed)
+ myData = processedData();
+ \endcode
+ is very inexpensive, since the first declaration and initialization
+ does not cause internal data to be created (only to be overwritten by the
+ assignment operation).
+
+ Since QGeometryData is explicitly shared, variables of type
+ QGeometryData behave like references, and the underlying data is modified
+ by calling a non-const function on any variable which shares that data.
+
+ To force an explicit copy call the detach() function.
+*/
+
+/*!
+ \typedef QGL::IndexArray
+
+ This is a convenience for either QArray<ushort> (OpenGL/ES) or
+ QArray<int> (desktop OpenGL).
+*/
+
+class QGeometryDataPrivate
+{
+public:
+ QGeometryDataPrivate();
+ ~QGeometryDataPrivate();
+ QGeometryDataPrivate *clone() const;
+
+ QBasicAtomicInt ref;
+
+ QVector3DArray vertices;
+ QVector3DArray normals;
+ QArray<QColor4ub> colors;
+ QList<QCustomDataArray> attributes;
+ QList<QVector2DArray> textures;
+ QGL::IndexArray indices;
+ QGLVertexBundle vertexBundle;
+ QGLIndexBuffer indexBuffer;
+ bool uploadsViable;
+ bool modified;
+ QBox3D bb;
+ static const int ATTR_CNT = 32;
+ quint32 fields;
+ qint8 key[ATTR_CNT];
+ quint8 size[ATTR_CNT];
+ int count;
+ int reserved;
+ bool boxValid;
+ QGeometryData::BufferStrategy bufferStrategy;
+};
+
+QGeometryDataPrivate::QGeometryDataPrivate()
+ : uploadsViable(true)
+ , modified(false)
+ , fields(0)
+ , count(0)
+ , reserved(-1)
+ , boxValid(true)
+ , bufferStrategy(QGeometryData::BufferIfPossible | QGeometryData::KeepClientData)
+{
+ ref = 0;
+ qMemSet(key, -1, ATTR_CNT);
+ qMemSet(size, 0, ATTR_CNT);
+}
+
+QGeometryDataPrivate::~QGeometryDataPrivate()
+{
+}
+
+QGeometryDataPrivate *QGeometryDataPrivate::clone() const
+{
+ QGeometryDataPrivate *temp = new QGeometryDataPrivate;
+ temp->vertices = vertices;
+ temp->normals = normals;
+ temp->colors = colors;
+ temp->attributes = attributes;
+ temp->textures = textures;
+ temp->indices = indices;
+ temp->vertexBundle = vertexBundle;
+ temp->indexBuffer = indexBuffer;
+ temp->uploadsViable = uploadsViable;
+ temp->modified = modified;
+ temp->bb = bb;
+ temp->fields = fields;
+ qMemCopy(temp->key, key, ATTR_CNT);
+ qMemCopy(temp->size, size, ATTR_CNT);
+ temp->count = count;
+ temp->reserved = reserved;
+ temp->boxValid = boxValid;
+ temp->bufferStrategy = bufferStrategy;
+ return temp;
+}
+
+/*!
+ \fn quint32 QGL::fieldMask(QGL::VertexAttribute attribute)
+ \relates QGeometryData
+ Returns an unsigned integer mask from the \a attribute.
+
+ \sa QGeometryData::fields()
+*/
+
+/*!
+ \enum QGeometryData::BufferStrategyFlags
+
+ This enum serves to describe how management of the data is handled
+ with respect to vertex buffer objects. The strategies are essentially a
+ combination of whether the client data is kept around after it has been
+ successfully uploaded to the GPU; and whether an upload is attempted at
+ all.
+
+ If the data set is very small it may be pointless to use up a VBO, hence
+ in this case KeepClientData may be used resulting in no attempt to upload
+ the data and client side arrays used instead.
+
+ \value InvalidStrategy No valid strategy has been specified.
+ \value KeepClientData Keep the client data, even after successful upload to the GPU.
+ \value BufferIfPossible Try to upload the data to the GPU.
+*/
+
+/*!
+ Construct an empty QGeometryData
+*/
+QGeometryData::QGeometryData()
+ : d(0)
+{
+}
+
+/*!
+ Construct QGeometryData as a copy of \a other
+*/
+QGeometryData::QGeometryData(const QGeometryData &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Construct an empty QGeometryData with the \a fields enabled.
+*/
+QGeometryData::QGeometryData(quint32 fields)
+ : d(new QGeometryDataPrivate)
+{
+ d->ref.ref();
+ const quint32 mask = 0x01;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (!(mask & fields)) continue;
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ enableField(attr);
+ }
+}
+
+/*!
+ Destroys this QGeometryData recovering any resources.
+*/
+QGeometryData::~QGeometryData()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Assigns this QGeometryData to be a copy of \a other.
+*/
+QGeometryData &QGeometryData::operator=(const QGeometryData &other)
+{
+ if (d != other.d)
+ {
+ if (d && !d->ref.deref())
+ delete d;
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ }
+ return *this;
+}
+
+/*!
+ Appends the geometry in \a data to this. If this is empty, then all
+ fields of \a data are appended; otherwise (when this has existing fields)
+ only those fields that exist in both are appended.
+
+ This does not change the indices - to reference the new geometry add
+ indices via the appendIndices() functions.
+*/
+void QGeometryData::appendGeometry(const QGeometryData &data)
+{
+ if (data.d && data.count())
+ {
+ detach();
+ d->modified = true;
+ d->boxValid = false;
+ int cnt = data.d->count;
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields | data.fields();
+ d->fields = fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ enableField(attr); // might not be enabled if we had NO fields
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ d->vertices.append(data.d->vertices);
+ else if (attr == QGL::Normal)
+ d->normals.append(data.d->normals);
+ else // colors
+ d->colors.append(data.d->colors);
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ d->textures[d->key[attr]].append(data.texCoords(attr));
+ }
+ else
+ {
+ d->attributes[d->key[attr]].append(data.attributes(attr));
+ }
+ }
+ }
+ d->count += cnt;
+ }
+}
+
+/*!
+ Appends all the data fields in QLogicalVertex \a v to this
+ QGeometryData object.
+*/
+int QGeometryData::appendVertex(const QLogicalVertex &v)
+{
+ create();
+ d->modified = true;
+ if (d->boxValid)
+ d->bb.unite(v.vertex());
+ quint32 fields = v.fields();
+ const quint32 mask = 0x01;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ appendVertex(v.vertex());
+ else if (attr == QGL::Normal)
+ appendNormal(v.normal());
+ else
+ appendColor(v.color());
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ appendTexCoord(v.texCoord(attr), attr);
+ }
+ else
+ {
+ appendAttribute(v.attribute(attr), attr);
+ }
+ }
+ }
+ return d->count - 1;
+}
+
+/*!
+ Returns a QLogicalVertex that references the \a{i}'th logical vertex
+ of this geometry.
+*/
+QLogicalVertex QGeometryData::logicalVertexAt(int i) const
+{
+ return QLogicalVertex(*this, i);
+}
+
+/*!
+ Normalize all the normal vectors in this geometry to unit length.
+*/
+void QGeometryData::normalizeNormals()
+{
+ check();
+ if (d) // nothng to do if its null
+ {
+ create();
+ d->modified = true;
+ if (hasField(QGL::Normal))
+ {
+ for (int i = 0; i < d->normals.count(); ++i)
+ d->normals[i].normalize();
+ }
+ }
+}
+
+/*!
+ Calculate and return a bounding box for the vertex data in this geometry.
+*/
+QBox3D QGeometryData::boundingBox() const
+{
+ QBox3D box;
+ if (d)
+ {
+ if (d->boxValid)
+ {
+ box = d->bb;
+ }
+ else
+ {
+ for (int i = 0; i < d->count; ++i)
+ box.unite(d->vertices.at(i));
+ d->bb = box;
+ }
+ }
+ return box;
+}
+
+/*!
+ Returns the coordinates of the center of the geometry.
+
+ The center is calculated as the centroid or geometric barycenter
+ of the vertices (the average of the vertices). For a convex hull this
+ is guaranteed to be inside the figure.
+*/
+QVector3D QGeometryData::center() const
+{
+ QVector3D center;
+ for (int i = 0; i < d->vertices.count(); ++i)
+ center += d->vertices.at(i);
+ return center / (float)d->vertices.count();
+}
+
+/*!
+ Returns a copy of this geometry data with elements in reverse order.
+*/
+QGeometryData QGeometryData::reversed() const
+{
+ QGeometryData r;
+ for (int i = count() - 1; i >= 0; --i)
+ r.appendVertex(logicalVertexAt(i));
+ return r;
+}
+
+/*!
+ Returns a copy of this geometry data with QGL::Position data translated by
+ the vector \a t. The other fields are unchanged.
+*/
+QGeometryData QGeometryData::translated(const QVector3D &t) const
+{
+ QGeometryData r(*this);
+ r.detach();
+ for (int i = 0; i < count(); ++i)
+ {
+ r.vertex(i) = r.vertexAt(i) + t;
+ }
+ return r;
+}
+
+/*!
+ Modifies this geometry data by generating texture data based on QGL::Position
+ values. If \a orientation is Qt::Horizontal (the default) then x-coordinate
+ values are generated, and y-coordinate values are set to 0.0; otherwise
+ y-coordinate values are generated and x-coordinate values are set to 0.0.
+ The values are appended to the texture coordinate \a field.
+
+ The method of calculation is based on the assumption that the vertex data
+ is a list of extents which span across the texture space horizontally, from
+ x = 0.0 to x = 1.0, in the case of Qt::Horizontal; or vertically in the
+ case of Qt::Vertical. The texture space of 1.0 is divided up proportionately
+ by the length of each extent.
+
+ \image texture-coords-gen.png
+
+ In this diagram the large blue numbers are the lengths of each extent, and
+ the texture coordinates generated are shown as \c{t(7/16, 1)} and so on.
+
+ Thus the texture coordinate t0 for vertex v0, is 0.0; t1 for vertex v1 is
+ \c{(v1 - v0).length() / totalLength} and so on.
+
+ The code to produce the texture coordinates for the quads in the image is:
+ \code
+ QGeometryData top;
+
+ // add data to the primitive
+ top.appendVertex(QVector3D(0.0, 0.0, 0.0));
+ top.appendVertex(QVector3D(6.0, 3.6, 0.0)); // (v1 - v0).length() = 7.0
+ top.appendVertex(QVector3D(10.0, 0.6, 0.0)); // (v2 - v1).length() = 5.0
+ top.appendVertex(QVector3D(13.0, 3.24, 0.0)); // (v3 - v2).length() = 4.0
+
+ // generate x (Qt::Horizontal) texture coordinates over the primitive
+ top.generateTextureCoordinates(); // spread over 7 + 5 + 4 = 16
+
+ // make a copy translated down, the copy has y texture coordinates all 0
+ QGeometryData bottom = top.translated(QVector3D(0, 0, -1));
+
+ // now modify the top so its y texture coordinates are all 1
+ for (int i = 0; i < top.count(); ++i)
+ top.texCoordRef(QGL::TextureCoord0).setY(1.0);
+
+ displayList->addQuadsZipped(top, bottom);
+ \endcode
+*/
+void QGeometryData::generateTextureCoordinates(Qt::Orientation orientation, QGL::VertexAttribute field)
+{
+ QArray<qreal> extents;
+ extents.append(0.0);
+ qreal totalExtents = 0.0;
+ QArray<QVector3D> v = vertices();
+ for (int i = 0; i < v.count() - 1; ++i)
+ {
+ int n = (i + 1) % v.count();
+ QVector3D e = v[n] - v[i];
+ qreal extent = e.length();
+ totalExtents += extent;
+ extents.append(totalExtents);
+ }
+ if (hasField(field))
+ clear(field);
+ if (orientation == Qt::Horizontal)
+ {
+ for (int i = 0; i < v.count(); ++i)
+ appendTexCoord(QVector2D(extents[i] / totalExtents, 0.0), field);
+ }
+ else
+ {
+ for (int i = 0; i < v.count(); ++i)
+ appendTexCoord(QVector2D(0.0, extents[i] / totalExtents), field);
+ }
+}
+
+/*!
+ Returns a QGeometryData instance containing alternating vertices from
+ this geometry and \a other. The resulting geometry contains N vertices
+ where \c{N == qMin(count(), other.count())}, and has only the fields
+ that are in both geometries.
+*/
+QGeometryData QGeometryData::interleavedWith(const QGeometryData &other) const
+{
+ QGeometryData res;
+ check();
+ other.check();
+ if (d && other.d)
+ {
+ int cnt = qMax(d->count, other.d->count);
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields & other.d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ res.enableField(attr);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ {
+ QArray<QVector3D> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->vertices.at(i));
+ tmp.append(other.d->vertices.at(i));
+ }
+ res.d->vertices = tmp;
+ }
+ else if (attr == QGL::Normal)
+ {
+ QArray<QVector3D> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->normals.at(i));
+ tmp.append(other.d->normals.at(i));
+ }
+ res.d->normals = tmp;
+ }
+ else // colors
+ {
+ QArray<QColor4ub> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->colors.at(i));
+ tmp.append(other.d->colors.at(i));
+ }
+ res.d->colors = tmp;
+ }
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ QArray<QVector2D> tmp;
+ const QArray<QVector2D> txa = d->textures.at(d->key[attr]);
+ const QArray<QVector2D> txb = other.d->textures.at(other.d->key[attr]);
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(txa.at(i));
+ tmp.append(txb.at(i));
+ }
+ res.d->textures[d->key[attr]] = tmp;
+ }
+ else
+ {
+ QCustomDataArray tmp;
+ const QCustomDataArray ata = d->attributes.at(d->key[attr]);
+ const QCustomDataArray atb = other.d->attributes.at(other.d->key[attr]);
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(ata.at(i));
+ tmp.append(atb.at(i));
+ }
+ res.d->attributes[d->key[attr]] = tmp;
+ }
+ }
+ }
+ res.d->count = cnt * 2;
+ }
+ return res;
+}
+
+/*!
+ Sets this QGeometryData to contain alternating vertices from
+ this geometry and \a other. The resulting geometry contains \c{N * 2} vertices
+ where \c{N == qMin(count(), other.count())}, and has only the fields
+ that are in both geometries.
+*/
+void QGeometryData::interleaveWith(const QGeometryData &other)
+{
+ check();
+ other.check();
+ if (d && other.d)
+ {
+ create();
+ d->modified = true;
+ d->boxValid = false;
+ int cnt = qMin(d->count, other.d->count);
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields & other.d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ {
+ QArray<QVector3D> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->vertices.at(i));
+ tmp.append(other.d->vertices.at(i));
+ }
+ d->vertices = tmp;
+ }
+ else if (attr == QGL::Normal)
+ {
+ QArray<QVector3D> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->normals.at(i));
+ tmp.append(other.d->normals.at(i));
+ }
+ d->normals = tmp;
+ }
+ else // colors
+ {
+ QArray<QColor4ub> tmp;
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(d->colors.at(i));
+ tmp.append(other.d->colors.at(i));
+ }
+ d->colors = tmp;
+ }
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ QArray<QVector2D> tmp;
+ const QArray<QVector2D> txa = d->textures.at(d->key[attr]);
+ const QArray<QVector2D> txb = other.d->textures.at(other.d->key[attr]);
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(txa.at(i));
+ tmp.append(txb.at(i));
+ }
+ d->textures[d->key[attr]] = tmp;
+ }
+ else
+ {
+ QCustomDataArray tmp;
+ const QCustomDataArray ata = d->attributes.at(d->key[attr]);
+ const QCustomDataArray atb = other.d->attributes.at(other.d->key[attr]);
+ for (int i = 0; i < cnt; ++i)
+ {
+ tmp.append(ata.at(i));
+ tmp.append(atb.at(i));
+ }
+ d->attributes[d->key[attr]] = tmp;
+ }
+ }
+ }
+ d->count = cnt * 2;
+ }
+}
+
+/*!
+ Clear all data structures. The actual fields are retained, but they
+ have no contents.
+ \code
+ QGeometryData data;
+ data.appendVertex(a);
+ data.appendTexCoord(t);
+
+ // prints "1"
+ qDebug() << data.count();
+
+ // x == a
+ QVector3D x = data.vertexAt(0);
+
+ quint32 flds = QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::TextureCoord0);
+ qDebug() << (flds == data.fields()); // prints "true"
+
+ data.clear();
+ qDebug() << data.count(); // prints "0"
+ QVector3D x = data.vertexAt(0); // asserts - no data in vertices
+ qDebug() << (flds == data.fields()); // still prints "true"
+ \endcode
+
+ To clear a specific field and its data use \c{data.clear(field)} below.
+
+ To clear all fields and data, simply set this to an empty geometry:
+ \code
+ data = QGeometryData();
+ \endcode
+ */
+void QGeometryData::clear()
+{
+ if (d)
+ {
+ create();
+ d->modified = true;
+ d->bb = QBox3D();
+ d->boxValid = true;
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ d->vertices.clear();
+ else if (attr == QGL::Normal)
+ d->normals.clear();
+ else
+ d->colors.clear();
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ d->textures[d->key[field]].clear();
+ }
+ else
+ {
+ d->attributes[d->key[field]].clear();
+ }
+ }
+ }
+ d->count = 0;
+ }
+}
+
+/*!
+ Clears the data from \a field, and removes the field. After this call
+ hasField() will return false for this field.
+*/
+void QGeometryData::clear(QGL::VertexAttribute field)
+{
+ if (d && (QGL::fieldMask(field) & d->fields))
+ {
+ create();
+ d->modified = true;
+ if (field == QGL::Position)
+ {
+ d->bb = QBox3D();
+ d->boxValid = true;
+ }
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ d->vertices.clear();
+ else if (attr == QGL::Normal)
+ d->normals.clear();
+ else
+ d->colors.clear();
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ d->textures[d->key[field]].clear();
+ }
+ else
+ {
+ d->attributes[d->key[field]].clear();
+ }
+ d->key[field] = -1;
+ d->fields = d->fields & ~QGL::fieldMask(field);
+ }
+}
+
+/*!
+ Sets the geometry data to handle an \a amount of data. This is generally
+ not required unless its anticipated that a large amount of data will be
+ appended and realloc overhead is desired to be avoided. If \a amount is
+ less than the amount already reserved, or if this object has
+ more than the \a amount of data items, then this function exits without
+ doing anything. This function will never delete data.
+*/
+void QGeometryData::reserve(int amount)
+{
+ if (d && (d->reserved > amount || d->reserved < d->count))
+ return;
+ create();
+ d->reserved = amount;
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ d->vertices.reserve(amount);
+ else if (attr == QGL::Normal)
+ d->normals.reserve(amount);
+ else
+ d->colors.reserve(amount);
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ d->textures[d->key[field]].reserve(amount);
+ }
+ else
+ {
+ d->attributes[d->key[field]].reserve(amount);
+ }
+ }
+ }
+}
+
+/*!
+ Draws this geometry on the \a painter, from \a start for \a count elements
+ in \a mode. The drawing \a mode is by default QGL::Triangles. This function
+ Also calls the upload() method to ensure that the geometry is resident on
+ the graphics hardware if appropriate.
+
+ If the geometry is a point or line, then the \a drawWidth value specified the
+ width/size of the line/point.
+*/
+void QGeometryData::draw(QGLPainter *painter, int start, int count, GLenum mode, qreal drawWidth)
+{
+ if (d && d->indices.size() && d->count)
+ {
+ upload();
+ painter->clearAttributes();
+ if (mode==QGL::Points) {
+#if !defined(QT_OPENGL_ES_2)
+ ::glPointSize(drawWidth);
+#endif
+ } else if (mode==QGL::LineStrip || mode == QGL::Lines) {
+ ::glLineWidth(drawWidth);
+ }
+ painter->setVertexBundle(d->vertexBundle);
+ if (count == 0)
+ count = d->indexBuffer.indexCount();
+ painter->draw(QGL::DrawingMode(mode), d->indexBuffer, start, count);
+ }
+}
+
+/*!
+ Uploads this geometry data to the graphics hardware if appropriate. If the
+ data is already uploaded and has not been modified since it was last
+ uploaded, then this function does nothing.
+
+ If the bufferStrategy() does not specify QGL::BufferIfPossible then this
+ function does nothing.
+
+ If the data was successfully uploaded, and the bufferStrategy() does not
+ specify QGL::KeepClientData then the data will be removed with a call to
+ the clear() function.
+
+ If the data was successfully uploaded, on this call or previously, then this
+ function will return true. Otherwise it returns false.
+*/
+bool QGeometryData::upload()
+{
+ bool vboUploaded = false;
+ bool iboUploaded = false;
+
+ if (!d)
+ return false;
+ if (!d->modified)
+ return d->vertexBundle.isUploaded() && d->indexBuffer.isUploaded();
+
+ check();
+
+ // Need to recreate the buffers from the modified data.
+ d->vertexBundle = QGLVertexBundle();
+ d->indexBuffer = QGLIndexBuffer();
+
+ // Copy the geometry data to the vertex buffer.
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (!(mask & fields))
+ continue;
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr == QGL::Position)
+ d->vertexBundle.addAttribute(attr, d->vertices);
+ else if (attr == QGL::Normal)
+ d->vertexBundle.addAttribute(attr, d->normals);
+ else if (attr == QGL::Color)
+ d->vertexBundle.addAttribute(attr, d->colors);
+ else if (attr < QGL::CustomVertex0)
+ d->vertexBundle.addAttribute(attr, d->textures.at(d->key[field]));
+ else
+ d->vertexBundle.addAttribute(attr, d->attributes.at(d->key[field]));
+ }
+
+ // Upload the buffer if requested, otherwise keep it client-side.
+ // Note: QGLVertexBundle will act as a client-side buffer if not uploaded.
+ if ((d->bufferStrategy & BufferIfPossible) != 0)
+ {
+ if (d->vertexBundle.upload())
+ vboUploaded = true;
+ }
+
+ // Copy the geometry data to the index buffer and upload if requested.
+ d->indexBuffer.setIndexes(d->indices);
+ if ((d->bufferStrategy & BufferIfPossible) != 0)
+ {
+ if (d->indexBuffer.upload())
+ iboUploaded = true;
+ }
+
+ d->modified = false;
+
+ if (!(d->bufferStrategy & KeepClientData) && vboUploaded && iboUploaded)
+ clear();
+
+ return vboUploaded && iboUploaded;
+}
+
+/*!
+ Sets the buffer \a strategy for this geometry.
+
+ \sa bufferStrategy()
+*/
+void QGeometryData::setBufferStrategy(QGeometryData::BufferStrategy strategy)
+{
+ if (!d || d->bufferStrategy != strategy)
+ {
+ create();
+ d->modified = true;
+ d->bufferStrategy = strategy;
+ }
+}
+
+/*!
+ Returns the buffer strategy for this geometry. The default is
+ \c{QGL::BufferIfPossible | QGL::KeepClientData}.
+
+ \sa setBufferStrategy()
+*/
+QGeometryData::BufferStrategy QGeometryData::bufferStrategy() const
+{
+ if (d)
+ return d->bufferStrategy;
+ return InvalidStrategy;
+}
+
+/*!
+ Returns a reference to the vertex buffer for this geometry.
+
+ \sa indexBuffer()
+*/
+QGLVertexBundle QGeometryData::vertexBundle() const
+{
+ return d->vertexBundle;
+}
+
+/*!
+ Returns a reference to the index buffer for this geometry.
+
+ \sa vertexBundle()
+*/
+QGLIndexBuffer QGeometryData::indexBuffer() const
+{
+ return d->indexBuffer;
+}
+
+/*!
+ Appends \a index to the vertex index array.
+
+ \sa appendIndices(), indices()
+*/
+void QGeometryData::appendIndex(int index)
+{
+ create();
+ d->modified = true;
+ d->indices.append(index);
+}
+
+/*!
+ Appends \a index1, \a index2, and \a index3 to the geometry's
+ index array.
+
+ \sa appendIndex(), indices()
+*/
+void QGeometryData::appendIndices(int index1, int index2, int index3)
+{
+ create();
+ d->modified = true;
+ d->indices.append(index1, index2, index3);
+}
+
+/*!
+ Returns the index array that was created by appendIndex().
+
+ \sa appendIndex(), appendIndices()
+*/
+QGL::IndexArray QGeometryData::indices() const
+{
+ if (d)
+ return d->indices;
+ else
+ return QGL::IndexArray();
+}
+
+/*!
+ Appends the \a indices to the geometry's index array.
+*/
+void QGeometryData::appendIndices(const QGL::IndexArray &indices)
+{
+ create();
+ d->modified = true;
+ d->indices.append(indices);
+}
+
+/*!
+ Append the point \a v0 to this geometry data as a position field.
+*/
+void QGeometryData::appendVertex(const QVector3D &v0)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Position);
+ d->vertices.append(v0);
+ if (d->boxValid)
+ d->bb.unite(v0);
+ d->count = qMax(d->count, d->vertices.count());
+}
+
+/*!
+ Append the points \a v0 and \a v1 to this geometry data as position fields.
+*/
+void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Position);
+ d->vertices.append(v0, v1);
+ if (d->boxValid)
+ {
+ d->bb.unite(v0);
+ d->bb.unite(v1);
+ }
+ d->count = qMax(d->count, d->vertices.count());
+}
+
+/*!
+ Append the points \a v0, \a v1 and \a v2 to this geometry data as position fields.
+*/
+void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Position);
+ d->vertices.append(v0, v1, v2);
+ if (d->boxValid)
+ {
+ d->bb.unite(v0);
+ d->bb.unite(v1);
+ d->bb.unite(v2);
+ }
+ d->count = qMax(d->count, d->vertices.count());
+}
+
+/*!
+ Append the points \a v0, \a v1, \a v2 and \a v3 to this geometry data as position fields.
+*/
+void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, const QVector3D &v3)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Position);
+ d->vertices.append(v0, v1, v2, v3);
+ if (d->boxValid)
+ {
+ d->bb.unite(v0);
+ d->bb.unite(v1);
+ d->bb.unite(v2);
+ d->bb.unite(v3);
+ }
+ d->count = qMax(d->count, d->vertices.count());
+}
+
+/*!
+ Append the float \a a0 to this geometry data, as an attribute \a field.
+*/
+void QGeometryData::appendAttribute(float a0, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->attributes[d->key[field]].append(a0);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the float \a a0 and \a a1 to this geometry data, as an attribute \a field.
+*/
+void QGeometryData::appendAttribute(float a0, float a1, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->attributes[d->key[field]].append(a0, a1);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the floats \a a0, \a a1 and \a a2 to this geometry data, as attribute \a field.
+*/
+void QGeometryData::appendAttribute(float a0, float a1, float a2, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->attributes[d->key[field]].append(a0, a1, a2);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the floats \a a0, \a a1, \a a2 and \a a3 to this geometry data, as attribute \a field.
+*/
+void QGeometryData::appendAttribute(float a0, float a1, float a2, float a3, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->attributes[d->key[field]].append(a0, a1, a2, a3);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the 2D point \a a to this geometry data, as an attribute \a field.
+*/
+void QGeometryData::appendAttribute(const QVector2D &a, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ if (d->attributes.at(d->key[field]).isEmpty())
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector2D);
+ d->attributes[d->key[field]].append(a);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the 3D point \a v to this geometry data, as an attribute \a field.
+*/
+void QGeometryData::appendAttribute(const QVector3D &v, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ if (d->attributes.at(d->key[field]).isEmpty())
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector3D);
+ d->attributes[d->key[field]].append(v);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the variant value \a a to this geometry data, as an attribute \a field.
+*/
+void QGeometryData::appendAttribute(const QVariant &a, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ if (d->attributes.at(d->key[field]).isEmpty())
+ {
+ // floats and doubles get handled "automatically" - float is default
+ if (a.type() == QVariant::Vector2D)
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector2D);
+ else if (a.type() == QVariant::Vector3D)
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector3D);
+ else if (a.type() == QVariant::Vector4D)
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector4D);
+ else if (a.type() == QVariant::Color)
+ d->attributes[d->key[field]].setElementType(QCustomDataArray::Color);
+ else
+ Q_ASSERT_X(false, "QGeometryData::appendAttribute", "bad type");
+ }
+ d->attributes[d->key[field]].append(a);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+}
+
+/*!
+ Append the vector \a n0 to this geometry data, as a lighting normal.
+*/
+void QGeometryData::appendNormal(const QVector3D &n0)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Normal);
+ d->normals.append(n0);
+ d->count = qMax(d->count, d->normals.count());
+}
+
+/*!
+ Append the vectors \a n0 and \a n1 to this geometry data, as lighting normals.
+*/
+void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Normal);
+ d->normals.append(n0, n1);
+ d->count = qMax(d->count, d->normals.count());
+}
+
+/*!
+ Append the vectors \a n0, \a n1 and \a n2 to this geometry data, as lighting normals.
+*/
+void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Normal);
+ d->normals.append(n0, n1, n2);
+ d->count = qMax(d->count, d->normals.count());
+}
+
+/*!
+ Append the vectors \a n0, \a n1, \a n2 and \a n3 to this geometry data, as lighting normals.
+*/
+void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2, const QVector3D &n3)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Normal);
+ d->normals.append(n0, n1, n2, n3);
+ d->count = qMax(d->count, d->normals.count());
+}
+
+/*!
+ Append the point \a t0 to this geometry data, as an texture \a field.
+*/
+void QGeometryData::appendTexCoord(const QVector2D &t0, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->textures[d->key[field]].append(t0);
+ d->count = qMax(d->count, d->textures[d->key[field]].count());
+}
+
+/*!
+ Append the points \a t0 and \a t1 to this geometry data, as texture \a{field}s.
+*/
+void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->textures[d->key[field]].append(t0, t1);
+ d->count = qMax(d->count, d->textures[d->key[field]].count());
+}
+
+/*!
+ Append the points \a t0, \a t1 and \a t2 to this geometry data, as texture \a{field}s.
+*/
+void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->textures[d->key[field]].append(t0, t1, t2);
+ d->count = qMax(d->count, d->textures[d->key[field]].count());
+}
+
+/*!
+ Append the points \a t0, \a t1, \a t2 and \a t3 to this geometry data, as texture \a{field}s.
+*/
+void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, const QVector2D &t3, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ enableField(field);
+ d->textures[d->key[field]].append(t0, t1, t2, t3);
+ d->count = qMax(d->count, d->textures[d->key[field]].count());
+}
+
+/*!
+ Append the color \a c0 to this geometry data, as an color field.
+*/
+void QGeometryData::appendColor(const QColor4ub &c0)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Color);
+ d->colors.append(c0);
+ d->count = qMax(d->count, d->colors.count());
+}
+
+/*!
+ Append the color \a c0 and \a c1 to this geometry data, as color fields.
+*/
+void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Color);
+ d->colors.append(c0, c1);
+ d->count = qMax(d->count, d->colors.count());
+}
+
+/*!
+ Append the color \a c0, \a c1 and \a c2 to this geometry data, as color fields.
+*/
+void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Color);
+ d->colors.append(c0, c1, c2);
+ d->count = qMax(d->count, d->colors.count());
+}
+
+/*!
+ Append the color \a c0, \a c1, \a c2 and \a c3 to this geometry data, as color fields.
+*/
+void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2, const QColor4ub &c3)
+{
+ create();
+ d->modified = true;
+ enableField(QGL::Color);
+ d->colors.append(c0, c1, c2, c3);
+ d->count = qMax(d->count, d->colors.count());
+}
+
+/*!
+ Append the points in \a ary to this geometry data as position fields.
+*/
+void QGeometryData::appendVertexArray(const QVector3DArray &ary)
+{
+ if (ary.count())
+ {
+ create();
+ d->modified = true;
+ d->boxValid = false;
+ enableField(QGL::Position);
+ d->vertices.append(ary);
+ d->count = qMax(d->count, d->vertices.count());
+ }
+}
+
+/*!
+ Append the points in \a ary to this geometry data, as an attribute \a field entries.
+*/
+void QGeometryData::appendAttributeArray(const QCustomDataArray &ary, QGL::VertexAttribute field)
+{
+ if (ary.count())
+ {
+ create();
+ d->modified = true;
+ enableField(field);
+ d->attributes[d->key[field]].append(ary);
+ d->count = qMax(d->count, d->attributes[d->key[field]].count());
+ }
+}
+
+/*!
+ Append the vectors in \a ary to this geometry data, as lighting normals.
+*/
+void QGeometryData::appendNormalArray(const QVector3DArray &ary)
+{
+ if (ary.count())
+ {
+ create();
+ d->modified = true;
+ enableField(QGL::Normal);
+ d->normals.append(ary);
+ d->count = qMax(d->count, d->normals.count());
+ }
+}
+
+/*!
+ Append the 2D points in \a ary to this geometry data, as texture \a field entries.
+*/
+void QGeometryData::appendTexCoordArray(const QVector2DArray &ary, QGL::VertexAttribute field)
+{
+ if (ary.count())
+ {
+ create();
+ d->modified = true;
+ enableField(field);
+ d->textures[d->key[field]].append(ary);
+ d->count = qMax(d->count, d->textures[d->key[field]].count());
+ }
+}
+
+/*!
+ Append the colors in \a ary to this geometry data, as color fields.
+*/
+void QGeometryData::appendColorArray(const QArray<QColor4ub> &ary)
+{
+ if (ary.count())
+ {
+ create();
+ d->modified = true;
+ enableField(QGL::Color);
+ d->colors.append(ary);
+ d->count = qMax(d->count, d->colors.count());
+ }
+}
+
+/*!
+ Returns a modifiable reference to the vertex data at index \a i.
+*/
+QVector3D &QGeometryData::vertex(int i)
+{
+ create();
+ d->modified = true;
+ d->boxValid = false;
+ return d->vertices[i];
+}
+
+/*!
+ Returns a copy of the vertex position data.
+*/
+QVector3DArray QGeometryData::vertices() const
+{
+ if (d)
+ return d->vertices;
+ return QArray<QVector3D>();
+}
+
+/*!
+ \internal
+ Returns a pointer to the vertex data.
+*/
+const QVector3DArray *QGeometryData::vertexData() const
+{
+ if (d)
+ return &d->vertices;
+ return 0;
+}
+
+
+/*!
+ Returns a non-modifiable reference to the vertex position data at index \a i.
+*/
+const QVector3D &QGeometryData::vertexAt(int i) const
+{
+ Q_ASSERT(hasField(QGL::Position));
+ return d->vertices.at(i);
+}
+
+/*!
+ Returns a modifiable reference to the normal data at index \a i.
+*/
+QVector3D &QGeometryData::normal(int i)
+{
+ create();
+ d->modified = true;
+ return d->normals[i];
+}
+
+/*!
+ Returns a non-modifiable reference to the normal data at index \a i.
+*/
+const QVector3D &QGeometryData::normalAt(int i) const
+{
+ Q_ASSERT(hasField(QGL::Normal));
+ return d->normals.at(i);
+}
+
+/*!
+ Returns a copy of the lighting normal data.
+*/
+QVector3DArray QGeometryData::normals() const
+{
+ if (d)
+ return d->normals;
+ return QArray<QVector3D>();
+}
+
+/*!
+ Returns a modifiable reference to the color data at index \a i.
+*/
+QColor4ub &QGeometryData::color(int i)
+{
+ create();
+ d->modified = true;
+ return d->colors[i];
+}
+
+/*!
+ Returns a non-modifiable reference to the color data at index \a i.
+*/
+const QColor4ub &QGeometryData::colorAt(int i) const
+{
+ Q_ASSERT(hasField(QGL::Color));
+ return d->colors.at(i);
+}
+
+/*!
+ Returns a copy of the color data.
+*/
+QArray<QColor4ub> QGeometryData::colors() const
+{
+ if (d)
+ return d->colors;
+ return QArray<QColor4ub>();
+}
+
+/*!
+ Returns a modifiable reference to the \a field texture coordinate data at index \a i.
+*/
+QVector2D &QGeometryData::texCoord(int i, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ return d->textures[d->key[field]][i];
+}
+
+/*!
+ Returns a copy of the \a field texture coordinate data.
+*/
+QVector2DArray QGeometryData::texCoords(QGL::VertexAttribute field) const
+{
+ return hasField(field) ? d->textures.at(d->key[field]) : QVector2DArray();
+}
+
+/*!
+ Returns a non-modifiable reference to the texture coordinate data at index \a i for \a field.
+*/
+const QVector2D &QGeometryData::texCoordAt(int i, QGL::VertexAttribute field) const
+{
+ Q_ASSERT(hasField(field));
+ return d->textures.at(d->key[field]).at(i);
+}
+
+/*!
+ Returns a modifiable reference to the float \a field attribute data at index \a i.
+*/
+float &QGeometryData::floatAttribute(int i, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ QCustomDataArray &ary = d->attributes[d->key[field]];
+ Q_ASSERT(ary.elementType() == QCustomDataArray::Float);
+ return ary.m_array[i];
+}
+
+/*!
+ Returns a modifiable reference to the 2D vector \a field attribute data at index \a i.
+*/
+QVector2D &QGeometryData::vector2DAttribute(int i, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ QCustomDataArray &ary = d->attributes[d->key[field]];
+ Q_ASSERT(ary.elementType() == QCustomDataArray::Vector2D);
+ float *data = ary.m_array.data();
+ QVector2D *v = reinterpret_cast<QVector2D*>(data + i*2);
+ return *v;
+}
+
+/*!
+ Returns a modifiable reference to the 3D vector \a field attribute data at index \a i.
+*/
+QVector3D &QGeometryData::vector3DAttribute(int i, QGL::VertexAttribute field)
+{
+ create();
+ d->modified = true;
+ QCustomDataArray &ary = d->attributes[d->key[field]];
+ Q_ASSERT(ary.elementType() == QCustomDataArray::Vector3D);
+ float *data = ary.m_array.data();
+ QVector3D *v = reinterpret_cast<QVector3D*>(data + i*2);
+ return *v;
+}
+
+/*!
+ Returns a copy of the \a field attribute data.
+*/
+QCustomDataArray QGeometryData::attributes(QGL::VertexAttribute field) const
+{
+ return hasField(field) ? d->attributes.at(d->key[field]) : QCustomDataArray();
+}
+
+/*!
+ Returns a copy of the float \a field attribute data at index \a i.
+*/
+float QGeometryData::floatAttributeAt(int i, QGL::VertexAttribute field) const
+{
+ Q_ASSERT(hasField(field));
+ return d->attributes.at(d->key[field]).floatAt(i);
+}
+
+/*!
+ Returns a copy of the 2D vector \a field attribute data at index \a i.
+*/
+QVector2D QGeometryData::vector2DAttributeAt(int i, QGL::VertexAttribute field) const
+{
+ Q_ASSERT(hasField(field));
+ return d->attributes.at(d->key[field]).vector2DAt(i);
+}
+
+/*!
+ Returns a copy of the 3D vector \a field attribute data at index \a i.
+*/
+QVector3D QGeometryData::vector3DAttributeAt(int i, QGL::VertexAttribute field) const
+{
+ Q_ASSERT(hasField(field));
+ return d->attributes.at(d->key[field]).vector3DAt(i);
+}
+
+/*!
+ Returns the attribute value for the \a field, suitable for passing
+ to QGLPainter.
+
+ \sa QGLPainter::setVertexAttribute()
+*/
+QGLAttributeValue QGeometryData::attributeValue(QGL::VertexAttribute field) const
+{
+ if (hasField(field))
+ {
+ if (field < QGL::TextureCoord0)
+ {
+ if (field == QGL::Position)
+ return QGLAttributeValue(d->vertices);
+ else if (field == QGL::Normal)
+ return QGLAttributeValue(d->normals);
+ else if (field == QGL::Color)
+ return QGLAttributeValue(d->colors);
+ }
+ else
+ {
+ if (field < QGL::CustomVertex0)
+ return QGLAttributeValue(d->textures.at(d->key[field]));
+ else
+ return QGLAttributeValue(d->attributes.at(d->key[field]));
+ }
+ }
+ return QGLAttributeValue();
+}
+
+/*!
+ Returns true if this geometry has the field corresponding to \a attr. Note
+ that it is still possible for no data to have been added for that field.
+*/
+bool QGeometryData::hasField(QGL::VertexAttribute attr) const
+{
+ if (d)
+ return d->key[attr] != -1;
+ return false;
+}
+
+/*!
+ Enables this geometry to contain data of type \a field. Generally it is
+ not necessary to call this function since it is called by all the append
+ functions.
+*/
+void QGeometryData::enableField(QGL::VertexAttribute field)
+{
+ if (d && d->key[field] != -1)
+ return;
+ create();
+ d->modified = true;
+ Q_ASSERT(field < d->ATTR_CNT); // don't expand that enum too much
+ d->fields |= (1 << field);
+ switch (field)
+ {
+ case QGL::Position:
+ d->key[QGL::Position] = 0;
+ d->size[QGL::Position] = 3;
+ if (d->reserved > 0)
+ d->vertices.reserve(d->reserved);
+ break;
+ case QGL::Normal:
+ d->key[QGL::Normal] = 1;
+ d->size[QGL::Normal] = 3;
+ if (d->reserved > 0)
+ d->normals.reserve(d->reserved);
+ break;
+ case QGL::Color:
+ d->key[QGL::Color] = 2;
+ d->size[QGL::Color] = 1;
+ if (d->reserved > 0)
+ d->colors.reserve(d->reserved);
+ break;
+ case QGL::TextureCoord0:
+ case QGL::TextureCoord1:
+ case QGL::TextureCoord2:
+ d->textures.append(QVector2DArray());
+ d->key[field] = d->textures.count() - 1;
+ d->size[field] = 2;
+ if (d->reserved > 0)
+ d->textures[d->key[field]].reserve(d->reserved);
+ break;
+ default:
+ // Custom and User vertex attributes.
+ d->attributes.append(QCustomDataArray());
+ d->key[field] = d->attributes.count() - 1;
+ d->size[field] = d->attributes.at(d->key[field]).elementSize();
+ if (d->reserved > 0)
+ d->attributes[d->key[field]].reserve(d->reserved);
+ break;
+ }
+}
+
+/*!
+ Return a bit-mask of the supported fields in this geometry. The
+ QGL::VertexAttribute enum can be recovered from this bit-mask by
+ \code
+ quint32 fields = fields();
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(fields);
+ \endcode
+*/
+quint32 QGeometryData::fields() const
+{
+ if (d)
+ return d->fields;
+ return 0;
+}
+
+/*!
+ Returns the count of logical vertices stored. This is effectively
+ the max() of QArray::count() over all of the enabled data types.
+*/
+int QGeometryData::count() const
+{
+ if (d)
+ return d->count;
+ return 0;
+}
+
+/*!
+ Returns the count of data stored in \a field. This will always be at
+ most count(), but could be less.
+*/
+int QGeometryData::count(QGL::VertexAttribute field) const
+{
+ int result = 0;
+ if (d && (QGL::fieldMask(field) & d->fields))
+ {
+ if (field < QGL::TextureCoord0)
+ {
+ if (field == QGL::Position)
+ result = d->vertices.count();
+ else if (field == QGL::Normal)
+ result = d->normals.count();
+ else
+ result = d->colors.count();
+ }
+ else if (field < QGL::CustomVertex0)
+ {
+ result = d->textures[d->key[field]].count();
+ }
+ else
+ {
+ result = d->attributes[d->key[field]].count();
+ }
+ }
+ return result;
+}
+
+/*!
+ Returns true if this geometry is identical to the \a other; and false otherwise.
+*/
+bool QGeometryData::operator==(const QGeometryData &other) const
+{
+ bool isEqual = false;
+ if (d)
+ {
+ if (d == other.d)
+ {
+ isEqual = true;
+ }
+ else
+ {
+ if (other.d && d->fields == other.d->fields && d->count == other.d->count)
+ {
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields;
+ isEqual = true;
+ for (int field = 0; fields && isEqual; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ isEqual = (d->vertices == other.d->vertices);
+ else if (attr == QGL::Normal)
+ isEqual = (d->normals == other.d->normals);
+ else // colors
+ isEqual = (d->colors == other.d->colors);
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ isEqual = (d->textures.at(d->key[attr]) == other.d->textures.at(d->key[attr]));
+ }
+ else
+ {
+ QArray<float> me = d->attributes.at(d->key[attr]).toFloatArray();
+ QArray<float> him = other.d->attributes.at(d->key[attr]).toFloatArray();
+ isEqual = (me == him);
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ isEqual = other.isNull();
+ }
+ return isEqual;
+}
+
+/*!
+ Returns true if this geometry is empty - that is it contains no vertices
+ or other data - and returns false otherwise. If an existing geometry has
+ been made empty by a call to clear() then this will be true (but isNull()
+ will be false).
+
+ \sa isNull()
+*/
+bool QGeometryData::isEmpty() const
+{
+ bool empty = true;
+ if (d)
+ empty = d->count == 0;
+ return empty;
+}
+
+/*!
+ Returns true if this geometry is uninitialized - that is it contains no
+ internal data structures. A newly constructed QGeometryData object is
+ null until some data is added or changed.
+
+ \sa isEmpty()
+*/
+bool QGeometryData::isNull() const
+{
+ return d == NULL;
+}
+
+/*!
+ Returns the number of index values stored in this geometry data.
+
+ This value is exactly the same as indices().size() (but does not
+ incur the copy).
+*/
+int QGeometryData::indexCount() const
+{
+ if (d)
+ return d->indices.size();
+ return 0;
+}
+
+void QGeometryData::create()
+{
+ if (!d) // lazy creation of data block
+ {
+ d = new QGeometryDataPrivate;
+ d->ref.ref();
+ }
+}
+
+/*!
+ Force this geometry to ensure it has its own unshared internal data
+ block, making a copy in the case that it is currently shared.
+*/
+void QGeometryData::detach()
+{
+ create();
+ if (d->ref > 1) // being shared, must detach
+ {
+ QGeometryDataPrivate *temp = d->clone();
+ d->ref.deref();
+ d = temp;
+ d->ref.ref();
+ }
+}
+
+/*!
+ \fn quint64 QGeometryData::id() const
+ Return an opaque value that can be used to identify which data block is
+ being used by this QGeometryData instance. See the class documentation
+ relating to explicit sharing.
+*/
+
+#ifndef QT_NO_DEBUG
+void QGeometryData::check() const
+{
+ if (!d)
+ return;
+ const quint32 mask = 0x01;
+ quint32 fields = d->fields;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ {
+ if (d->vertices.count() < d->count)
+ qWarning("QGeometryData - expected %d vertices, only %d found!",
+ d->count, d->vertices.count());
+ }
+ else if (attr == QGL::Normal)
+ {
+ if (d->normals.count() < d->count)
+ qWarning("QGeometryData - expected %d normals, only %d found!",
+ d->count, d->normals.count());
+ }
+ else
+ {
+ if (d->colors.count() < d->count)
+ qWarning("QGeometryData - expected %d colors, only %d found!",
+ d->count, d->colors.count());
+ }
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ if (d->textures.at(d->key[field]).count() < d->count)
+ qWarning("QGeometryData - expected %d texture coordinates for"
+ "QGL::TextureCoord%d, only %d found!",
+ d->count, field - QGL::TextureCoord0, d->textures.at(d->key[field]).count());
+ }
+ else
+ {
+ if (d->attributes.at(d->key[field]).count() < d->count)
+ qWarning("QGeometryData - expected %d attributes for"
+ "QGL::CustomVertex%d, only %d found!",
+ d->count, field - QGL::CustomVertex0, d->attributes.at(d->key[field]).count());
+ }
+ }
+ }
+}
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QGeometryData &vertices)
+{
+ dbg << "QGeometryData" << &vertices << " size:" << vertices.count()
+#ifndef QT_NO_DEBUG
+ << "data block id:" << vertices.id()
+#endif
+ ;
+ quint32 fields = vertices.fields();
+ const quint32 mask = 0x01;
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ {
+ dbg << " vertices:" << vertices.count(attr);
+ dbg << vertices.vertices();
+ }
+ else if (attr == QGL::Normal)
+ {
+ dbg << " normals:" << vertices.count(attr);
+ dbg << vertices.normals();
+ }
+ else
+ {
+ dbg << " colors:" << vertices.count(attr);
+ dbg << vertices.colors();
+ }
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ dbg << " textures:" << (attr - QGL::TextureCoord0) << vertices.count(attr);
+ dbg << vertices.texCoords(attr);
+ }
+ else
+ {
+ dbg << " custom:" << (attr - QGL::CustomVertex0) << vertices.count(attr);
+ dbg << vertices.texCoords(attr);
+ }
+ }
+ }
+ if (vertices.indexCount() > 0)
+ {
+ dbg << " indices:" << vertices.indices();
+ }
+ return dbg;
+}
+#endif
diff --git a/src/threed/geometry/qgeometrydata.h b/src/threed/geometry/qgeometrydata.h
new file mode 100644
index 000000000..051915e32
--- /dev/null
+++ b/src/threed/geometry/qgeometrydata.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGEOMETRYDATA_H
+#define QGEOMETRYDATA_H
+
+#include "qcolor4ub.h"
+#include "qglnamespace.h"
+#include "qglindexbuffer.h"
+#include "qglvertexbundle.h"
+#include "qglattributevalue.h"
+#include "qcustomdataarray.h"
+#include "qbox3d.h"
+#include "qarray.h"
+#include "qvector2darray.h"
+#include "qvector3darray.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGeometryDataPrivate;
+class QLogicalVertex;
+class QGLPainter;
+
+namespace QGL
+{
+ inline quint32 fieldMask(QGL::VertexAttribute f) { return (quint32)0x01 << f; }
+
+#if defined(QT_OPENGL_ES)
+ typedef QArray<ushort> IndexArray;
+#else
+ typedef QArray<uint> IndexArray;
+#endif
+};
+
+class Q_QT3D_EXPORT QGeometryData
+{
+public:
+ QGeometryData();
+ QGeometryData(const QGeometryData &);
+ QGeometryData(quint32 fields);
+ ~QGeometryData();
+
+ QGeometryData &operator=(const QGeometryData &);
+
+ void appendGeometry(const QGeometryData &data);
+ int appendVertex(const QLogicalVertex &v);
+ void normalizeNormals();
+ QBox3D boundingBox() const;
+ QVector3D center() const;
+
+ QGeometryData reversed() const;
+ QGeometryData translated(const QVector3D &) const;
+ void generateTextureCoordinates(Qt::Orientation orientation = Qt::Horizontal,
+ QGL::VertexAttribute attribute = QGL::TextureCoord0);
+ QGeometryData interleavedWith(const QGeometryData &other) const;
+ void interleaveWith(const QGeometryData &other);
+ void clear();
+ void clear(QGL::VertexAttribute);
+ void reserve(int amount);
+ void draw(QGLPainter *painter, int start, int count, GLenum mode = QGL::Triangles, qreal drawWidth=1.0);
+ bool upload();
+ enum BufferStrategyFlags
+ {
+ InvalidStrategy = 0x00,
+ KeepClientData = 0x01,
+ BufferIfPossible = 0x02,
+ };
+ Q_DECLARE_FLAGS(BufferStrategy, BufferStrategyFlags)
+ void setBufferStrategy(BufferStrategy strategy);
+ BufferStrategy bufferStrategy() const;
+ QGLVertexBundle vertexBundle() const;
+ QGLIndexBuffer indexBuffer() const;
+
+ void appendIndex(int index);
+ void appendIndices(int index1, int index2, int index3);
+ void appendIndices(const QGL::IndexArray &indices);
+ QGL::IndexArray indices() const;
+
+ void appendVertex(const QVector3D &v0);
+ void appendVertex(const QVector3D &v0, const QVector3D &v1);
+ void appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2);
+ void appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, const QVector3D &v3);
+
+ void appendAttribute(float a, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(float a, float b, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(float a, float b, float c, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(float a, float b, float c, float d, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(const QVector2D &a, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(const QVector3D &a, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendAttribute(const QVariant &a, QGL::VertexAttribute field = QGL::CustomVertex0);
+
+ void appendNormal(const QVector3D &n0);
+ void appendNormal(const QVector3D &n0, const QVector3D &n1);
+ void appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2);
+ void appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2, const QVector3D &n3);
+
+ void appendTexCoord(const QVector2D &t0, QGL::VertexAttribute field = QGL::TextureCoord0);
+ void appendTexCoord(const QVector2D &t0, const QVector2D &t1, QGL::VertexAttribute field = QGL::TextureCoord0);
+ void appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, QGL::VertexAttribute field = QGL::TextureCoord0);
+ void appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, const QVector2D &t3, QGL::VertexAttribute field = QGL::TextureCoord0);
+
+ void appendColor(const QColor4ub &c0);
+ void appendColor(const QColor4ub &c0, const QColor4ub &c1);
+ void appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2);
+ void appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2, const QColor4ub &c3);
+
+ void appendVertexArray(const QVector3DArray &ary);
+ void appendAttributeArray(const QCustomDataArray &ary, QGL::VertexAttribute field = QGL::CustomVertex0);
+ void appendNormalArray(const QVector3DArray &ary);
+ void appendTexCoordArray(const QVector2DArray &ary, QGL::VertexAttribute field = QGL::TextureCoord0);
+ void appendColorArray(const QArray<QColor4ub> &ary);
+
+ QLogicalVertex logicalVertexAt(int i) const;
+
+ QVector3DArray vertices() const;
+ QVector3D &vertex(int i);
+ const QVector3D &vertexAt(int i) const;
+
+ QVector3DArray normals() const;
+ QVector3D &normal(int i);
+ const QVector3D &normalAt(int i) const;
+
+ QArray<QColor4ub> colors() const;
+ QColor4ub &color(int i);
+ const QColor4ub &colorAt(int i) const;
+
+ QVector2DArray texCoords(QGL::VertexAttribute field = QGL::TextureCoord0) const;
+ QVector2D &texCoord(int i, QGL::VertexAttribute field = QGL::TextureCoord0);
+ const QVector2D &texCoordAt(int i, QGL::VertexAttribute field = QGL::TextureCoord0) const;
+
+ float &floatAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0);
+ QVector2D &vector2DAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0);
+ QVector3D &vector3DAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0);
+ QCustomDataArray attributes(QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ float floatAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ QVector2D vector2DAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ QVector3D vector3DAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const;
+
+ QGLAttributeValue attributeValue(QGL::VertexAttribute field) const;
+ bool hasField(QGL::VertexAttribute field) const;
+ void enableField(QGL::VertexAttribute field);
+ quint32 fields() const;
+ int count() const;
+ int count(QGL::VertexAttribute field) const;
+ int indexCount() const;
+ bool operator==(const QGeometryData &other) const;
+ bool isEmpty() const;
+ bool isNull() const;
+ void detach();
+#ifndef QT_NO_DEBUG
+ quint64 id() const { return (quint64)d; }
+#endif
+protected:
+ const QVector3DArray *vertexData() const;
+private:
+ void create();
+#ifndef QT_NO_DEBUG
+ void check() const;
+#else
+ void check() const {}
+#endif
+ friend class QLogicalVertex;
+
+ QGeometryDataPrivate *d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QGeometryData::BufferStrategy);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGeometryData &vertices);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QGEOMETRYDATA_H
diff --git a/src/threed/geometry/qglbezierpatches.cpp b/src/threed/geometry/qglbezierpatches.cpp
new file mode 100644
index 000000000..49842c653
--- /dev/null
+++ b/src/threed/geometry/qglbezierpatches.cpp
@@ -0,0 +1,815 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglbezierpatches.h"
+#include "qglbuilder.h"
+#include "qray3d.h"
+#include "qtriangle3d.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLBezierPatches
+ \brief The QGLBezierPatches class represents 3D geometry as a set of Bezier bicubic patches.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ Bezier bicubic patches represent a curved 3D surface by four fixed
+ control points at indices 0, 3, 12, and 15, together with twelve
+ additional floating control points that define the surface
+ curvature. Bezier geometry objects are made up of one or more
+ such patches to define the surface of an object.
+
+ The application specifies the vertex position data to the
+ constructor, and can optionally provide an index array.
+ The class interprets groups of 16 vertices as the control
+ points for successive patches.
+
+ A mesh defined by QGLBezierPatches is subdivided into flat
+ triangles for rendering when the \c{<<} operator is used
+ to add the patches to a QGLBuilder.
+
+ Many curved 3D objects can be defined as being made up of Bezier
+ bicubic patches, stitched together into a mesh. The most famous
+ Bezier bicubic object is probably the classic 3D "Utah Teapot",
+ first rendered in 1975. The QGLTeapot class provides a built-in
+ implementation of this object for testing purposes.
+
+ If texture co-ordinates are supplied via setTextureCoords(),
+ then patch texture co-ordinates will be derived from the
+ specified values as the patches are subdivided. Otherwise,
+ QGLBezierPatches will generate texture co-ordinates for each
+ patch based on the default square from (0, 0) to (1, 1).
+ The first vertex in the patch corresponds to (0, 0),
+ and the opposite vertex in the patch corresponds to (1, 1).
+
+ \sa QGLBuilder, QGLTeapot
+*/
+
+class QGLBezierPatchesPrivate
+{
+public:
+ QGLBezierPatchesPrivate()
+ : subdivisionDepth(4) {}
+ QGLBezierPatchesPrivate(const QGLBezierPatchesPrivate *other)
+ : positions(other->positions)
+ , textureCoords(other->textureCoords)
+ , subdivisionDepth(other->subdivisionDepth) {}
+
+ void copy(const QGLBezierPatchesPrivate *other)
+ {
+ positions = other->positions;
+ textureCoords = other->textureCoords;
+ subdivisionDepth = other->subdivisionDepth;
+ }
+
+ void subdivide(QGLBuilder *list) const;
+ qreal intersection
+ (const QRay3D &ray, bool anyIntersection, QVector2D *texCoord, int *patch) const;
+
+ QVector3DArray positions;
+ QVector2DArray textureCoords;
+ int subdivisionDepth;
+};
+
+// Temporary patch data for performing sub-divisions.
+class QGLBezierPatch
+{
+public:
+ // Control points for this mesh.
+ QVector3D points[16];
+
+ // Triangle mesh indices of the control points at each corner.
+ int indices[4];
+
+ QVector3D normal(qreal s, qreal t) const;
+ void convertToTriangles
+ (QGeometryData *prim,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex);
+ void subDivide(QGLBezierPatch &patch1, QGLBezierPatch &patch2,
+ QGLBezierPatch &patch3, QGLBezierPatch &patch4);
+ void createNewCorners(QGLBezierPatch &patch1, QGLBezierPatch &patch2,
+ QGLBezierPatch &patch3, QGLBezierPatch &patch4,
+ QGeometryData *prim,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex);
+ void recursiveSubDivide
+ (QGeometryData *prim,
+ int depth, qreal xtex, qreal ytex, qreal wtex, qreal htex);
+ qreal intersection
+ (qreal result, int depth, const QRay3D &ray, bool anyIntersection,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex, QVector2D *tc);
+};
+
+static int const cornerOffsets[] = {0, 3, 12, 15};
+static qreal const cornerS[] = {0.0f, 1.0f, 0.0f, 1.0f};
+static qreal const cornerT[] = {0.0f, 0.0f, 1.0f, 1.0f};
+
+// Helper functions for calculating the components of the Bernstein
+// polynomial and its derivative that make up the surface.
+static inline qreal b0(qreal v)
+{
+ return (1.0f - v) * (1.0f - v) * (1.0f - v);
+}
+static inline qreal b1(qreal v)
+{
+ return 3.0f * v * (1.0f - v) * (1.0f - v);
+}
+static inline qreal b2(qreal v)
+{
+ return 2.0f * v * v * (1.0f - v);
+}
+static inline qreal b3(qreal v)
+{
+ return v * v * v;
+}
+static inline qreal db0(qreal v)
+{
+ return -3.0f * (1.0f - v) * (1.0f - v);
+}
+static inline qreal db1(qreal v)
+{
+ return -6.0f * v * (1.0f - v) + 3.0f * (1.0f - v) * (1.0f - v);
+}
+static inline qreal db2(qreal v)
+{
+ return -3.0f * v * v + 6.0f * v * (1.0f - v);
+}
+static inline qreal db3(qreal v)
+{
+ return 3.0f * v * v;
+}
+
+// Compute the normal at a specific point in the patch.
+// The s and t values vary between 0 and 1.
+QVector3D QGLBezierPatch::normal(qreal s, qreal t) const
+{
+ qreal a[4];
+ qreal b[4];
+ qreal tx, ty, tz;
+ qreal sx, sy, sz;
+
+ // Compute the derivative of the surface in t.
+ a[0] = b0(s);
+ a[1] = b1(s);
+ a[2] = b2(s);
+ a[3] = b3(s);
+ b[0] = db0(t);
+ b[1] = db1(t);
+ b[2] = db2(t);
+ b[3] = db3(t);
+ tx = 0.0f;
+ ty = 0.0f;
+ tz = 0.0f;
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ tx += a[i] * points[j * 4 + i].x() * b[j];
+ ty += a[i] * points[j * 4 + i].y() * b[j];
+ tz += a[i] * points[j * 4 + i].z() * b[j];
+ }
+ }
+
+ // Compute the derivative of the surface in s.
+ a[0] = db0(s);
+ a[1] = db1(s);
+ a[2] = db2(s);
+ a[3] = db3(s);
+ b[0] = b0(t);
+ b[1] = b1(t);
+ b[2] = b2(t);
+ b[3] = b3(t);
+ sx = 0.0f;
+ sy = 0.0f;
+ sz = 0.0f;
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ sx += a[i] * points[j * 4 + i].x() * b[j];
+ sy += a[i] * points[j * 4 + i].y() * b[j];
+ sz += a[i] * points[j * 4 + i].z() * b[j];
+ }
+ }
+
+ // The normal is the cross-product of the two derivatives,
+ // normalized to a unit vector.
+ QVector3D n = QVector3D::normal(QVector3D(sx, sy, sz), QVector3D(tx, ty, tz));
+ if (n.isNull()) {
+ // A zero normal may occur if one of the patch edges is zero-length.
+ // We correct for this by substituting an overall patch normal that
+ // we compute from two of the sides that are not zero in length.
+ QVector3D sides[4];
+ QVector3D vectors[2];
+ sides[0] = points[3] - points[0];
+ sides[1] = points[15] - points[3];
+ sides[2] = points[12] - points[15];
+ sides[3] = points[0] - points[12];
+ int i = 0;
+ int j = 0;
+ vectors[0] = QVector3D(1.0f, 0.0f, 0.0f);
+ vectors[1] = QVector3D(0.0f, 1.0f, 0.0f);
+ while (i < 2 && j < 4) {
+ if (sides[j].isNull())
+ ++j;
+ else
+ vectors[i++] = sides[j++];
+ }
+ n = QVector3D::normal(vectors[0], vectors[1]);
+ }
+ return n;
+}
+
+// Convert this patch into flat triangles.
+void QGLBezierPatch::convertToTriangles
+ (QGeometryData *prim,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex)
+{
+ // The edges are considered ok if they have a non-zero length.
+ // Zero-length edges can occur in triangular-shaped patches.
+ // There is no point generating triangles along such edges.
+ bool edge1ok = (points[0] != points[3]);
+ bool edge2ok = (points[0] != points[12]);
+ bool edge3ok = (points[12] != points[15]);
+ bool edge4ok = (points[15] != points[3]);
+
+ // Find the mid-point on the patch by averaging the corners.
+ QVector3D mid = (points[0] + points[3] + points[12] + points[15]) / 4.0f;
+
+ // Allocate a triangle mesh vertex for the mid-point.
+ int midIndex = prim->count();
+ prim->appendVertex(mid);
+ prim->appendNormal(normal(0.5f, 0.5f));
+ prim->appendTexCoord
+ (QVector2D(xtex + wtex / 2.0f, ytex + htex / 2.0f));
+
+ // Divide the patch into 4 triangles pointing at the center.
+ if (edge1ok)
+ prim->appendIndices(indices[0], indices[1], midIndex);
+ if (edge2ok)
+ prim->appendIndices(indices[2], indices[0], midIndex);
+ if (edge3ok)
+ prim->appendIndices(indices[3], indices[2], midIndex);
+ if (edge4ok)
+ prim->appendIndices(indices[1], indices[3], midIndex);
+}
+
+// Sub-divide a Bezier curve (p1, p2, p3, p4) into two new
+// Bezier curves (l1, l2, l3, l4) and (r1, r2, r3, r4).
+static void subDivideBezierCurve
+ (const QVector3D &p1, const QVector3D &p2,
+ const QVector3D &p3, const QVector3D &p4,
+ QVector3D &l1, QVector3D &l2, QVector3D &l3, QVector3D &l4,
+ QVector3D &r1, QVector3D &r2, QVector3D &r3, QVector3D &r4)
+{
+ l1 = p1;
+ l2 = (p1 + p2) / 2.0f;
+ QVector3D h = (p2 + p3) / 2.0f;
+ l3 = (l2 + h) / 2.0f;
+ r3 = (p3 + p4) / 2.0f;
+ r2 = (h + r3) / 2.0f;
+ l4 = r1 = (l3 + r2) / 2.0f;
+ r4 = p4;
+}
+
+// Sub-divide this patch into four new patches. The triangle mesh
+// is used to allocate vertices for the corners of the new patches.
+void QGLBezierPatch::subDivide
+ (QGLBezierPatch &patch1, QGLBezierPatch &patch2,
+ QGLBezierPatch &patch3, QGLBezierPatch &patch4)
+{
+ // Sub-divide the Bezier curves for the control rows to create
+ // four rows of 8 control points. These define the left and
+ // right halves of the patch.
+ QVector3D row1[8];
+ QVector3D row2[8];
+ QVector3D row3[8];
+ QVector3D row4[8];
+ subDivideBezierCurve
+ (points[0], points[1], points[2], points[3],
+ row1[0], row1[1], row1[2], row1[3], row1[4], row1[5], row1[6], row1[7]);
+ subDivideBezierCurve
+ (points[4], points[5], points[6], points[7],
+ row2[0], row2[1], row2[2], row2[3], row2[4], row2[5], row2[6], row2[7]);
+ subDivideBezierCurve
+ (points[8], points[9], points[10], points[11],
+ row3[0], row3[1], row3[2], row3[3], row3[4], row3[5], row3[6], row3[7]);
+ subDivideBezierCurve
+ (points[12], points[13], points[14], points[15],
+ row4[0], row4[1], row4[2], row4[3], row4[4], row4[5], row4[6], row4[7]);
+
+ // Now sub-divide the 8 columns to create the four new patches.
+ subDivideBezierCurve
+ (row1[0], row2[0], row3[0], row4[0],
+ patch1.points[0], patch1.points[4], patch1.points[8], patch1.points[12],
+ patch3.points[0], patch3.points[4], patch3.points[8], patch3.points[12]);
+ subDivideBezierCurve
+ (row1[1], row2[1], row3[1], row4[1],
+ patch1.points[1], patch1.points[5], patch1.points[9], patch1.points[13],
+ patch3.points[1], patch3.points[5], patch3.points[9], patch3.points[13]);
+ subDivideBezierCurve
+ (row1[2], row2[2], row3[2], row4[2],
+ patch1.points[2], patch1.points[6], patch1.points[10], patch1.points[14],
+ patch3.points[2], patch3.points[6], patch3.points[10], patch3.points[14]);
+ subDivideBezierCurve
+ (row1[3], row2[3], row3[3], row4[3],
+ patch1.points[3], patch1.points[7], patch1.points[11], patch1.points[15],
+ patch3.points[3], patch3.points[7], patch3.points[11], patch3.points[15]);
+ subDivideBezierCurve
+ (row1[4], row2[4], row3[4], row4[4],
+ patch2.points[0], patch2.points[4], patch2.points[8], patch2.points[12],
+ patch4.points[0], patch4.points[4], patch4.points[8], patch4.points[12]);
+ subDivideBezierCurve
+ (row1[5], row2[5], row3[5], row4[5],
+ patch2.points[1], patch2.points[5], patch2.points[9], patch2.points[13],
+ patch4.points[1], patch4.points[5], patch4.points[9], patch4.points[13]);
+ subDivideBezierCurve
+ (row1[6], row2[6], row3[6], row4[6],
+ patch2.points[2], patch2.points[6], patch2.points[10], patch2.points[14],
+ patch4.points[2], patch4.points[6], patch4.points[10], patch4.points[14]);
+ subDivideBezierCurve
+ (row1[7], row2[7], row3[7], row4[7],
+ patch2.points[3], patch2.points[7], patch2.points[11], patch2.points[15],
+ patch4.points[3], patch4.points[7], patch4.points[11], patch4.points[15]);
+}
+
+void QGLBezierPatch::createNewCorners
+ (QGLBezierPatch &patch1, QGLBezierPatch &patch2,
+ QGLBezierPatch &patch3, QGLBezierPatch &patch4,
+ QGeometryData *prim,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex)
+{
+ // Add vertices for the new patch corners we have created.
+ qreal hwtex = wtex / 2.0f;
+ qreal hhtex = htex / 2.0f;
+ int topPointIndex = prim->count();
+ int leftPointIndex = topPointIndex + 1;
+ int midPointIndex = topPointIndex + 2;
+ int rightPointIndex = topPointIndex + 3;
+ int bottomPointIndex = topPointIndex + 4;
+
+ prim->appendVertex(patch1.points[3]);
+ prim->appendNormal(normal(0.5f, 0.0f));
+ prim->appendTexCoord(QVector2D(xtex + hwtex, ytex));
+
+ prim->appendVertex(patch1.points[12]);
+ prim->appendNormal(normal(0.0f, 0.5f));
+ prim->appendTexCoord(QVector2D(xtex, ytex + hhtex));
+
+ prim->appendVertex(patch1.points[15]);
+ prim->appendNormal(normal(0.5f, 0.5f));
+ prim->appendTexCoord(QVector2D(xtex + hwtex, ytex + hhtex));
+
+ prim->appendVertex(patch2.points[15]);
+ prim->appendNormal(normal(1.0f, 0.5f));
+ prim->appendTexCoord(QVector2D(xtex + wtex, ytex + hhtex));
+
+ prim->appendVertex(patch3.points[15]);
+ prim->appendNormal(normal(0.5f, 1.0f));
+ prim->appendTexCoord(QVector2D(xtex + hwtex, ytex + htex));
+
+ // Copy the indices for the corners of the new patches.
+ patch1.indices[0] = indices[0];
+ patch1.indices[1] = topPointIndex;
+ patch1.indices[2] = leftPointIndex;
+ patch1.indices[3] = midPointIndex;
+ patch2.indices[0] = topPointIndex;
+ patch2.indices[1] = indices[1];
+ patch2.indices[2] = midPointIndex;
+ patch2.indices[3] = rightPointIndex;
+ patch3.indices[0] = leftPointIndex;
+ patch3.indices[1] = midPointIndex;
+ patch3.indices[2] = indices[2];
+ patch3.indices[3] = bottomPointIndex;
+ patch4.indices[0] = midPointIndex;
+ patch4.indices[1] = rightPointIndex;
+ patch4.indices[2] = bottomPointIndex;
+ patch4.indices[3] = indices[3];
+}
+
+// Recursively sub-divide a patch into triangles.
+void QGLBezierPatch::recursiveSubDivide
+ (QGeometryData *prim,
+ int depth, qreal xtex, qreal ytex, qreal wtex, qreal htex)
+{
+ if (depth <= 1) {
+ convertToTriangles(prim, xtex, ytex, wtex, htex);
+ } else {
+ QGLBezierPatch patch1, patch2, patch3, patch4;
+ subDivide(patch1, patch2, patch3, patch4);
+ createNewCorners(patch1, patch2, patch3, patch4, prim, xtex, ytex, wtex, htex);
+ --depth;
+ qreal hwtex = wtex / 2.0f;
+ qreal hhtex = htex / 2.0f;
+ patch1.recursiveSubDivide(prim, depth, xtex, ytex, hwtex, hhtex);
+ patch2.recursiveSubDivide(prim, depth, xtex + hwtex, ytex, hwtex, hhtex);
+ patch3.recursiveSubDivide(prim, depth, xtex, ytex + hhtex, hwtex, hhtex);
+ patch4.recursiveSubDivide(prim, depth, xtex + hwtex, ytex + hhtex, hwtex, hhtex);
+ }
+}
+
+void QGLBezierPatchesPrivate::subdivide(QGLBuilder *list) const
+{
+ QGeometryData prim;
+ int count = positions.size();
+ for (int posn = 0; (posn + 15) < count; posn += 16) {
+ // Construct a QGLBezierPatch object from the next high-level patch.
+ QGLBezierPatch patch;
+ int vertex;
+ for (int vertex = 0; vertex < 16; ++vertex)
+ patch.points[vertex] = positions[posn + vertex];
+ QVector2D tex1, tex2;
+ if (!textureCoords.isEmpty()) {
+ tex1 = textureCoords[(posn / 16) * 2];
+ tex2 = textureCoords[(posn / 16) * 2 + 1];
+ } else {
+ tex1 = QVector2D(0.0f, 0.0f);
+ tex2 = QVector2D(1.0f, 1.0f);
+ }
+ qreal xtex = tex1.x();
+ qreal ytex = tex1.y();
+ qreal wtex = tex2.x() - xtex;
+ qreal htex = tex2.y() - ytex;
+ for (int corner = 0; corner < 4; ++corner) {
+ vertex = posn + cornerOffsets[corner];
+ QVector3D n = patch.normal(cornerS[corner], cornerT[corner]);
+ patch.indices[corner] = prim.count();
+ prim.appendVertex(patch.points[cornerOffsets[corner]]);
+ prim.appendNormal(n);
+ prim.appendTexCoord
+ (QVector2D(xtex + wtex * cornerS[corner],
+ ytex + htex * cornerT[corner]));
+ }
+
+ // Subdivide the patch and generate the final triangles.
+ patch.recursiveSubDivide(&prim, subdivisionDepth,
+ xtex, ytex, wtex, htex);
+ }
+ list->addTriangles(prim);
+}
+
+static inline qreal combineResults(qreal result, qreal t)
+{
+ if (qIsNaN(result))
+ return t;
+ if (t >= 0.0f)
+ return result < 0.0f ? t : qMin(result, t);
+ else
+ return result >= 0.0f ? result : qMax(result, t);
+}
+
+qreal QGLBezierPatch::intersection
+ (qreal result, int depth, const QRay3D& ray, bool anyIntersection,
+ qreal xtex, qreal ytex, qreal wtex, qreal htex, QVector2D *tc)
+{
+ // Check the convex hull of the patch for an intersection.
+ // If no intersection with the convex hull, then there is
+ // no point subdividing this patch further.
+ QBox3D box;
+ for (int point = 0; point < 16; ++point)
+ box.unite(points[point]);
+ if (!box.intersects(ray))
+ return result;
+
+ // Are we at the lowest point of subdivision yet?
+ if (depth <= 1) {
+ // Divide the patch into two triangles and intersect with those.
+ QTriangle3D triangle1(points[0], points[3], points[12]);
+ qreal t = triangle1.intersection(ray);
+ if (!qIsNaN(t)) {
+ result = combineResults(result, t);
+ if (result == t) {
+ QVector2D uv = triangle1.uv(ray.point(t));
+ QVector2D tp(xtex, ytex);
+ QVector2D tq(xtex + wtex, ytex);
+ QVector2D tr(xtex, ytex + htex);
+ *tc = uv.x() * tp + uv.y() * tq + (1 - uv.x() - uv.y()) * tr;
+ }
+ } else {
+ QTriangle3D triangle2(points[3], points[15], points[12]);
+ qreal t = triangle2.intersection(ray);
+ if (!qIsNaN(t)) {
+ result = combineResults(result, t);
+ if (result == t) {
+ QVector2D uv = triangle2.uv(ray.point(t));
+ QVector2D tp(xtex + wtex, ytex);
+ QVector2D tq(xtex + wtex, ytex + htex);
+ QVector2D tr(xtex, ytex + htex);
+ *tc = uv.x() * tp + uv.y() * tq + (1 - uv.x() - uv.y()) * tr;
+ }
+ }
+ }
+ } else {
+ // Subdivide the patch to find the point of intersection.
+ QGLBezierPatch patch1, patch2, patch3, patch4;
+ subDivide(patch1, patch2, patch3, patch4);
+ --depth;
+ qreal hwtex = wtex / 2.0f;
+ qreal hhtex = htex / 2.0f;
+ result = patch1.intersection
+ (result, depth, ray, anyIntersection,
+ xtex, ytex, hwtex, hhtex, tc);
+ if (anyIntersection && !qIsNaN(result))
+ return result;
+ result = patch2.intersection
+ (result, depth, ray, anyIntersection,
+ xtex + hwtex, ytex, hwtex, hhtex, tc);
+ if (anyIntersection && !qIsNaN(result))
+ return result;
+ result = patch3.intersection
+ (result, depth, ray, anyIntersection,
+ xtex, ytex + hhtex, hwtex, hhtex, tc);
+ if (anyIntersection && !qIsNaN(result))
+ return result;
+ result = patch4.intersection
+ (result, depth, ray, anyIntersection,
+ xtex + hwtex, ytex + hhtex, hwtex, hhtex, tc);
+ }
+ return result;
+}
+
+qreal QGLBezierPatchesPrivate::intersection
+ (const QRay3D &ray, bool anyIntersection, QVector2D *texCoord, int *bestPatch) const
+{
+ int count = positions.size();
+ qreal result = qSNaN();
+ QVector2D tc;
+ if (bestPatch)
+ *bestPatch = -1;
+ for (int posn = 0; (posn + 15) < count; posn += 16) {
+ QGLBezierPatch patch;
+ for (int vertex = 0; vertex < 16; ++vertex)
+ patch.points[vertex] = positions[posn + vertex];
+ QVector2D tex1, tex2;
+ if (!textureCoords.isEmpty()) {
+ tex1 = textureCoords[(posn / 16) * 2];
+ tex2 = textureCoords[(posn / 16) * 2 + 1];
+ } else {
+ tex1 = QVector2D(0.0f, 0.0f);
+ tex2 = QVector2D(1.0f, 1.0f);
+ }
+ qreal xtex = tex1.x();
+ qreal ytex = tex1.y();
+ qreal wtex = tex2.x() - xtex;
+ qreal htex = tex2.y() - ytex;
+ qreal prev = result;
+ result = patch.intersection
+ (result, subdivisionDepth, ray, anyIntersection,
+ xtex, ytex, wtex, htex, &tc);
+ if (bestPatch && result != prev)
+ *bestPatch = posn / 16;
+ if (anyIntersection && !qIsNaN(result))
+ break;
+ }
+ if (texCoord && !qIsNaN(result))
+ *texCoord = tc;
+ return result;
+}
+
+/*!
+ Constructs an empty Bezier patch list.
+
+ \sa setPositions()
+*/
+QGLBezierPatches::QGLBezierPatches()
+ : d_ptr(new QGLBezierPatchesPrivate())
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+
+ \sa operator=()
+*/
+QGLBezierPatches::QGLBezierPatches(const QGLBezierPatches &other)
+ : d_ptr(new QGLBezierPatchesPrivate(other.d_ptr.data()))
+{
+}
+
+/*!
+ Destroys this Bezier patch list.
+*/
+QGLBezierPatches::~QGLBezierPatches()
+{
+}
+
+/*!
+ Assigns \a other to this Bezier patch list.
+*/
+QGLBezierPatches &QGLBezierPatches::operator=
+ (const QGLBezierPatches &other)
+{
+ if (this != &other)
+ d_ptr->copy(other.d_ptr.data());
+ return *this;
+}
+
+/*!
+ Returns the positions of the vertices in the Bezier patches.
+
+ \sa setPositions(), textureCoords()
+*/
+QVector3DArray QGLBezierPatches::positions() const
+{
+ Q_D(const QGLBezierPatches);
+ return d->positions;
+}
+
+/*!
+ Sets the \a positions of the vertices in the Bezier patches.
+
+ \sa positions(), setTextureCoords()
+*/
+void QGLBezierPatches::setPositions(const QVector3DArray &positions)
+{
+ Q_D(QGLBezierPatches);
+ d->positions = positions;
+}
+
+/*!
+ Returns the texture co-ordinates for the Bezier patches.
+ Each patch consumes two elements from the texture
+ co-ordinate array, defining the opposite corners.
+
+ The default is an empty array, which indicates that each
+ patch will generate texture co-ordinates in the range
+ (0, 0) to (1, 1).
+
+ \sa setTextureCoords(), positions()
+*/
+QVector2DArray QGLBezierPatches::textureCoords() const
+{
+ Q_D(const QGLBezierPatches);
+ return d->textureCoords;
+}
+
+/*!
+ Sets the texture co-ordinates for the Bezier patches to
+ the elements of \a textureCoords. Each patch consumes
+ two elements from \a textureCoords, defining the opposite
+ corners.
+
+ If \a textureCoords is empty, then each patch will generate
+ texture co-ordinates in the range (0, 0) to (1, 1).
+
+ \sa textureCoords(), setPositions()
+*/
+void QGLBezierPatches::setTextureCoords(const QVector2DArray &textureCoords)
+{
+ Q_D(QGLBezierPatches);
+ d->textureCoords = textureCoords;
+}
+
+/*!
+ Returns the depth of subdivision to use when converting the
+ Bezier geometry into triangles. The default value is 4.
+
+ \sa setSubdivisionDepth()
+*/
+int QGLBezierPatches::subdivisionDepth() const
+{
+ Q_D(const QGLBezierPatches);
+ return d->subdivisionDepth;
+}
+
+/*!
+ Sets the depth of subdivision to use when converting the
+ Bezier geometry into triangles to \a value.
+
+ \sa subdivisionDepth()
+*/
+void QGLBezierPatches::setSubdivisionDepth(int value)
+{
+ Q_D(QGLBezierPatches);
+ d->subdivisionDepth = value;
+}
+
+/*!
+ Transforms the positions() in this Bezier geometry object
+ according to \a matrix.
+
+ \sa transformed()
+*/
+void QGLBezierPatches::transform(const QMatrix4x4 &matrix)
+{
+ Q_D(QGLBezierPatches);
+ d->positions.transform(matrix);
+}
+
+/*!
+ Returns a new Bezier geometry object that results from transforming
+ this object's positions() according to \a matrix.
+
+ \sa transform()
+*/
+QGLBezierPatches QGLBezierPatches::transformed(const QMatrix4x4 &matrix) const
+{
+ QGLBezierPatches result(*this);
+ result.d_ptr->positions.transform(matrix);
+ return result;
+}
+
+/*!
+ Returns true if \a ray intersects this Bezier geometry object;
+ false otherwise.
+
+ \sa intersection()
+*/
+bool QGLBezierPatches::intersects(const QRay3D &ray) const
+{
+ Q_D(const QGLBezierPatches);
+ return !qIsNaN(d->intersection(ray, true, 0, 0));
+}
+
+/*!
+ Returns the t value at which \a ray intersects this Bezier
+ geometry object, or not-a-number if there is no intersection.
+
+ When the \a ray intersects this object, the return value is a
+ parametric value that can be passed to QRay3D::point() to determine
+ the actual intersection point, as shown in the following example:
+
+ \code
+ qreal t = patches.intersection(ray);
+ QVector3D pt;
+ if (qIsNaN(t)) {
+ qWarning("no intersection occurred");
+ else
+ pt = ray.point(t);
+ \endcode
+
+ If \a ray intersects the object multiple times, the returned
+ t will be the smallest t value, corresponding to the first
+ intersection of the \a ray with the object. The t value may
+ be negative if the first intersection occurs in the reverse
+ direction of \a ray.
+
+ The intersection is determined by subdividing the patches into
+ triangles and intersecting with those triangles. A pruning
+ algorithm is used to discard patches whose convex hull do not
+ intersect with \a ray.
+
+ If \a texCoord is not null, then it will return the texture
+ co-ordinate of the intersection point.
+
+ If \a patch is not null, then it will return the index of the
+ patch that contains the intersection, or -1 if there is no
+ intersection.
+
+ \sa intersects()
+*/
+qreal QGLBezierPatches::intersection(const QRay3D &ray, QVector2D *texCoord, int *patch) const
+{
+ Q_D(const QGLBezierPatches);
+ return d->intersection(ray, false, texCoord, patch);
+}
+
+/*!
+ \relates QGLBezierPatches
+
+ Subdivides the Bezier patch data in \a patches into triangles
+ and adds them to the specified display \a list.
+*/
+QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches)
+{
+ patches.d_ptr->subdivide(&list);
+ return list;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/geometry/qglbezierpatches.h b/src/threed/geometry/qglbezierpatches.h
new file mode 100644
index 000000000..3bab8f5ff
--- /dev/null
+++ b/src/threed/geometry/qglbezierpatches.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLBEZIERPATCHES_H
+#define QGLBEZIERPATCHES_H
+
+#include "qvector2darray.h"
+#include "qvector3darray.h"
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLBezierPatchesPrivate;
+class QGLBuilder;
+class QRay3D;
+
+class Q_QT3D_EXPORT QGLBezierPatches
+{
+public:
+ QGLBezierPatches();
+ QGLBezierPatches(const QGLBezierPatches &other);
+ virtual ~QGLBezierPatches();
+
+ QGLBezierPatches &operator=(const QGLBezierPatches &other);
+
+ QVector3DArray positions() const;
+ void setPositions(const QVector3DArray &positions);
+
+ QVector2DArray textureCoords() const;
+ void setTextureCoords(const QVector2DArray &textureCoords);
+
+ int subdivisionDepth() const;
+ void setSubdivisionDepth(int value);
+
+ void transform(const QMatrix4x4 &matrix);
+ QGLBezierPatches transformed(const QMatrix4x4 &matrix) const;
+
+ bool intersects(const QRay3D &ray) const;
+ qreal intersection(const QRay3D &ray, QVector2D *texCoord = 0, int *patch = 0) const;
+
+private:
+ QScopedPointer<QGLBezierPatchesPrivate> d_ptr;
+
+ Q_DECLARE_PRIVATE(QGLBezierPatches)
+
+ friend Q_QT3D_EXPORT QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches);
+};
+
+Q_QT3D_EXPORT QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/geometry/qglbuilder.cpp b/src/threed/geometry/qglbuilder.cpp
new file mode 100644
index 000000000..d3be13899
--- /dev/null
+++ b/src/threed/geometry/qglbuilder.cpp
@@ -0,0 +1,1378 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglbuilder.h"
+#include "qglbuilder_p.h"
+#include "qglsection_p.h"
+#include "qglmaterialcollection.h"
+#include "qglpainter.h"
+#include "qgeometrydata.h"
+#include "qvector_utils_p.h"
+
+#include <QtGui/qvector2d.h>
+
+#include <QtCore/qdebug.h>
+
+/*!
+ \class QGLBuilder
+ \brief The QGLBuilder class constructs geometry for efficient display.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ \tableofcontents
+
+ Use a QGLBuilder to build up vertex, index, texture and other data
+ during application initialization. The finalizedSceneNode() function
+ returns an optimized scene which can be efficiently and flexibly
+ displayed during frames of rendering. It is suited to writing loaders
+ for 3D models, and for programatically creating geometry.
+
+ \section1 Geometry Building
+
+ QGLBuilder makes the job of getting triangles on the GPU simple. It
+ calculates indices and normals for you, then uploads the data. While
+ it has addQuads() and other functions to deal with quads, all data is
+ represented as triangles for portability.
+
+ The simplest way to use QGLBuilder is to send a set of geometry
+ values to it using QGeometryData in the constructor:
+
+ \code
+ MyView::MyView() : QGLView()
+ {
+ // in the constructor construct a builder on the stack
+ QGLBuilder builder;
+ QGeometryData triangle;
+ QVector3D a(2, 2, 0);
+ QVector3D b(-2, 2, 0);
+ QVector3D c(0, -2, 0);
+ triangle.appendVertex(a, b, c);
+
+ // When adding geometry, QGLBuilder automatically creates lighting normals
+ builder << triangle;
+
+ // obtain the scene from the builder
+ m_scene = builder.finalizedSceneNode();
+
+ // apply effects at app initialization time
+ QGLMaterial *mat = new QGLMaterial;
+ mat->setDiffuseColor(Qt::red);
+ m_scene->setMaterial(mat);
+ }
+ \endcode
+
+ Then during rendering the scene is used to display the results:
+ \code
+ MyView::paintGL(QGLPainter *painter)
+ {
+ m_scene->draw(painter);
+ }
+ \endcode
+
+ QGLBuilder automatically generates index values and normals
+ on-the-fly during geometry building. During building, simply send
+ primitives to the builder as a sequence of vertices, and
+ vertices that are the same will be referenced by a single index
+ automatically.
+
+ Primitives will have standard normals generated automatically
+ based on vertex winding.
+
+ Consider the following code for OpenGL to draw a quad with corner
+ points A, B, C and D :
+
+ \code
+ float vertices[12] =
+ {
+ -1.0, -1.0, -1.0, // A
+ 1.0, -1.0, -1.0, // B
+ 1.0, 1.0, 1.0, // C
+ -1.0, 1.0, 1.0 // D
+ };
+ float normals[12] = { 0.0f };
+ for (int i = 0; i < 12; i += 3)
+ {
+ normals[i] = 0.0;
+ normals[i+1] = -sqrt(2.0);
+ normals[i+2] = sqrt(2.0);
+ }
+ GLuint indices[6] = {
+ 0, 1, 2, // triangle A-B-C
+ 0, 2, 3 // triangle A-C-D
+ };
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, vertices);
+ glNormalPointer(3, GL_FLOAT, 0, normals);
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
+ \endcode
+
+ With QGLBuilder this code becomes:
+
+ \code
+ float vertices[12] =
+ {
+ -1.0, -1.0, -1.0, // A
+ 1.0, -1.0, -1.0, // B
+ 1.0, 1.0, 1.0, // C
+ -1.0, 1.0, 1.0 // D
+ };
+ QGLBuilder quad;
+ QGeometryData data;
+ data.appendVertexArray(QArray<QVector3D>::fromRawData(
+ reinterpret_cast<const QVector3D*>(vertices), 4));
+ quad.addQuads(data);
+ \endcode
+
+ The data primitive is added to the list, as two triangles, indexed to
+ removed the redundant double storage of B & C - just the same as the
+ OpenGL code.
+
+ QGLBuilder will also calculate a normal for the quad and apply it
+ to the vertices.
+
+ In this trivial example the indices are easily calculated, however
+ in more complex geometry it is easy to introduce bugs by trying
+ to manually control indices. Extra work is required to generate,
+ track and store the index values correctly.
+
+ Bugs such as trying to index two vertices with different data -
+ one with texture data and one without - into one triangle can
+ easily result. The picture becomes more difficult when smoothing
+ groups are introduced - see below.
+
+ Using indices is always preferred since it saves space on the GPU,
+ and makes the geometry perform faster during application run time.
+
+ \section2 Removing Epsilon Errors
+
+ Where vertices are generated by modelling packages or tools, or
+ during computation in code, very frequently rounding errors will
+ result in several vertices being generated that are actually
+ the same vertex but are separated by tiny amounts. At best these
+ duplications waste space on the GPU but at worst can introduce
+ visual artifacts that mar the image displayed.
+
+ Closing paths, generating solids of rotation, or moving model
+ sections out and back can all introduce these types of epsilon
+ errors, resulting in "cracks" or artifacts on display.
+
+ QGLBuilder's index generation process uses a fuzzy match that
+ coalesces all vertex values at a point - even if they are out by
+ a tiny amount - and references them with a single index.
+
+ \section2 Lighting Normals and Null Triangles
+
+ QGLBuilder functions calculate lighting normals, when building
+ geometry. This saves the application programmer from having to write
+ code to calculate them. Normals for each triangle (a, b, c) are
+ calculated as the QVector3D::normal(a, b, c).
+
+ If lighting normals are explicitly supplied when using QGLBuilder,
+ then this calculation is not done. This may save on build time.
+
+ As an optimization, QGLBuilder skips null triangles, that is ones
+ with zero area, where it can. Such triangles generate no fragments on
+ the GPU, and thus do not display but nonetheless can take up space
+ and processing power.
+
+ Null triangles can easily occur when calculating vertices results
+ in two vertices coinciding, or three vertices lying on the same line.
+
+ This skipping is done using the lighting normals cross-product. If the
+ cross-product is a null vector then the triangle is null.
+
+ When lighting normals are specified explicitly the skipping
+ optimization is suppressed, so if for some reason null triangles are
+ required to be retained, then specify normals for each logical vertex.
+
+ See the documentation below of the individual addTriangle() and other
+ functions for more details.
+
+ \section2 Raw Triangle Mode
+
+ Where generation of indices and normals is not needed - for example if
+ porting an existing application, it is possible to do a raw import of
+ triangle data, without using any of QGLBuilder's processing.
+
+ To do this ensure that indices are placed in the QGeometryData passed to
+ the addTriangles() function, and this will trigger \bold{raw triangle} mode.
+
+ When adding triangles in this way ensure that all appropriate values
+ have been correctly set, and that the normals, indices and other data
+ are correctly calculated, since no checking is done.
+
+ When writing new applications, simply leave construction of normals and
+ indices to the QGLBuilder
+
+ \section1 Rendering and QGLSceneNode items.
+
+ QGLSceneNodes are used to manage application of local transformations,
+ materials and effects.
+
+ QGLBuilder generates a root level QGLSceneNode, which can be accessed
+ with the sceneNode() function. Under this a new node is created for
+ each section of geometry, and also by using pushNode() and popNode().
+
+ To organize geometry for painting with different materials and effects
+ call the newNode() function:
+
+ \code
+ QGLSceneNode *box = builder.newNode();
+ box->setMaterial(wood);
+ \endcode
+
+ Many nodes may be created this way, but they will be optimized into
+ a small number of buffers under the one scene when the
+ finalizedSceneNode() function is called.
+
+ \image soup.png
+
+ Here the front can is a set of built geometry and the other two are
+ scene nodes that reference it, without copying any geometry.
+
+ \snippet builder/builder.cpp 0
+
+ QGLSceneNodes can be used after the builder is created to cheaply
+ copy and redisplay the whole scene. Or to reference parts of the geometry
+ use the functions newNode() or pushNode() and popNode() to manage
+ QGLSceneNode generation while building geometry.
+
+ To draw the resulting built geometry simply call the draw method of the
+ build geometry.
+
+ \snippet builder/builder.cpp 1
+
+ Call the \l{QGLSceneNode::palette()}{palette()} function on the sceneNode()
+ to get the QGLMaterialCollection for the node, and place textures
+ and materials into it.
+
+ Built geometry will typically share the one palette. Either create a
+ palette, and pass it to the \l{QGLBuilder::QGLBuilder()}{constructor};
+ or pass no arguments to the constructor and the QGLBuilder
+ will create a palette:
+
+ \snippet builder/builder.cpp 2
+
+ These may then be applied as needed throughout the building of the
+ geometry using the integer reference, \c{canMat} in the above code.
+
+ See the QGLSceneNode documentation for more.
+
+ \section1 Using Sections
+
+ During initialization of the QGLBuilder, while accumulating
+ geometry, the geometry data in a QGLBuilder is placed into
+ sections - there must be at least one section.
+
+ Call the newSection() function to create a new section:
+
+ \snippet builder/builder.cpp 3
+
+ Here separate sections for the rounded outside cylinder and flat top and
+ bottom of the soup can model makes for the appearance of a sharp edge
+ between them. If the sides and top and bottom were in the same section
+ QGLBuilder would attempt to average the normals around the edge resulting
+ in an unrealistic effect.
+
+ In 3D applications this concept is referred to as
+ \l{http://www.google.com/search?smoothing+groups}{smoothing groups}. Within
+ a section (smoothing group) all normals are averaged making it appear
+ as one smoothly shaded surface.
+
+ The can has 3 smoothing groups - bottom, top and sides.
+
+ This mesh of a Q is a faceted model - it has 0 smoothing groups:
+
+ \image faceted-q.png
+
+ To create geometry with a faceted appearance call newSection() with
+ an argument of QGL::Faceted thus \c{newSection(QGL::Faceted)}.
+
+ Faceted geometry is suitable for small models, where hard edges are
+ desired between every face - a dice, gem or geometric solid for example.
+
+ If no section has been created when geometry is added a new section is
+ created automatically. This section will have its smoothing set
+ to QGL::Smooth.
+
+ To create a faceted appearance rather than accepting the automatically
+ created section the << operator can also be used:
+
+ \code
+ QGLBuilder builder;
+ QGeometryData triangles;
+ triangles.appendVertices(a, b, c);
+ builder << QGL::Faceted << triangles;
+ \endcode
+
+ \section2 Geometry Data in a Section
+
+ Management of normals and vertices for smoothing, and other data is
+ handled automatically by the QGLBuilder instance.
+
+ Within a section, incoming geometry data will be coalesced and
+ indices created to reference the fewest possible copies of the vertex
+ data. For example, in smooth geometry all copies of a vertex are
+ coalesced into one, and referenced by indices.
+
+ One of the few exceptions to this is the case where texture data forms
+ a \i seam and a copy of a vertex must be created to carry the two
+ texture coordinates either side of the seam.
+
+ \image texture-seam.png
+
+ Coalescing has the effect of packing geometry data into the
+ smallest space possible thus improving cache coherence and performance.
+
+ Again all this is managed automatically by QGLBuilder and all
+ that is required is to create smooth or faceted sections, and add
+ geometry to them.
+
+ Each QGLSection references a contiguous range of vertices in a
+ QGLBuilder.
+
+ \section1 Finalizing and Retrieving the Scene
+
+ Once the geometry has been accumulated in the QGLBuilder instance, the
+ finalizedSceneNode() method must be called to retrieve the optimized
+ scene. This function serves to normalize the geometry and optimize
+ it for display.
+
+ While it may be convenient to get pointers to sub nodes in the scene
+ during construction, it is important to retrieve the root of the scene
+ so that the memory consumed by the scene can be recovered. The builder
+ will create a QGLMaterialCollection; and there may be geometry, materials
+ and other resources: these are all parented onto the root scene node.
+ These can easily be recovered by deleting the root scene node:
+
+ \code
+ MyView::MyView() : QGLView()
+ {
+ // in the constructor construct a builder on the stack
+ QGLBuilder builder;
+
+ // add geometry as shown above
+ builder << triangles;
+
+ // obtain the scene from the builder & take ownership
+ m_scene = builder.finalizedSceneNode();
+ }
+
+ MyView::~MyView()
+ {
+ // recover all scene resources
+ delete m_scene;
+ }
+ \endcode
+
+ Alternatively set the scene's parent to ensure resource recovery
+ \c{m_scene->setParent(this)}.
+
+
+*/
+
+QGLBuilderPrivate::QGLBuilderPrivate(QGLBuilder *parent)
+ : currentSection(0)
+ , currentNode(0)
+ , rootNode(0)
+ , defThreshold(5)
+ , q(parent)
+{
+}
+
+QGLBuilderPrivate::~QGLBuilderPrivate()
+{
+ qDeleteAll(sections);
+ if (rootNode)
+ {
+ qWarning("Destroying QGLBuilder but finalizedSceneNode() not called");
+ delete rootNode;
+ }
+}
+
+/*!
+ Construct a new QGLBuilder using \a materials for the palette. If the
+ \a materials argument is null, then a new palette is created.
+*/
+QGLBuilder::QGLBuilder(QGLMaterialCollection *materials)
+ : dptr(new QGLBuilderPrivate(this))
+{
+ dptr->rootNode = new QGLSceneNode;
+ if (!materials)
+ materials = new QGLMaterialCollection(dptr->rootNode);
+ dptr->rootNode->setPalette(materials);
+}
+
+/*!
+ Destroys this QGLBuilder recovering any resources.
+*/
+QGLBuilder::~QGLBuilder()
+{
+ delete dptr;
+}
+
+/*!
+ Helper function to calculate the normal for and set it on vertices
+ in \a i, \a j and \a k in triangle data \a p. If the triangle in
+ data \a p is a null triangle (area == 0) then the function returns
+ false, otherwise it returns true.
+*/
+static inline void setNormals(int i, int j, int k, QGeometryData &p, const QVector3D &n)
+{
+ p.normal(i) = n;
+ p.normal(j) = n;
+ p.normal(k) = n;
+}
+
+static bool qCalculateNormal(int i, int j, int k, QGeometryData &p, QVector3D *vec = 0)
+{
+ QVector3D norm;
+ QVector3D *n = &norm;
+ if (vec)
+ n = vec;
+ bool nullTriangle = false;
+ *n = QVector3D::crossProduct(p.vertexAt(j) - p.vertexAt(i),
+ p.vertexAt(k) - p.vertexAt(j));
+ if (qFskIsNull(n->x()))
+ n->setX(0.0f);
+ if (qFskIsNull(n->y()))
+ n->setY(0.0f);
+ if (qFskIsNull(n->z()))
+ n->setZ(0.0f);
+ if (n->isNull())
+ {
+ nullTriangle = true;
+ }
+ else
+ {
+ setNormals(i, j, k, p, *n);
+ }
+ return nullTriangle;
+}
+
+/*!
+ \internal
+ Helper function to actually add the vertices to geometry.
+*/
+void QGLBuilderPrivate::addTriangle(int i, int j, int k,
+ const QGeometryData &p, int &count)
+{
+ if (currentSection == 0)
+ q->newSection();
+ QLogicalVertex a(p, i);
+ QLogicalVertex b(p, j);
+ QLogicalVertex c(p, k);
+ currentSection->append(a, b, c);
+ count += 3;
+}
+
+/*!
+ Add \a triangles - a series of one or more triangles - to this builder.
+
+ The data is broken into groups of 3 vertices, each processed as a triangle.
+
+ If \a triangles has less than 3 vertices this function exits without
+ doing anything. Any vertices at the end of the list under a multiple
+ of 3 are ignored.
+
+ If no normals are supplied in \a triangles, a normal is calculated; as
+ the cross-product \c{(b - a) x (c - a)}, for each group of 3
+ logical vertices \c{a(triangle, i), b(triangle, i+1), c(triangle, i+2)}.
+
+ In the case of a degenerate triangle, where the cross-product is null,
+ that triangle is skipped. Supplying normals suppresses this behaviour
+ (and means any degenerate triangles will be added to the geometry).
+
+ \bold{Raw Triangle Mode}
+
+ If \a triangles has indices specified then no processing of any kind is
+ done and all the geometry is simply dumped in to the builder.
+
+ This \bold{raw triangle} mode is for advanced use, and it is assumed that
+ the user knows what they are doing, in particular that the indices
+ supplied are correct, and normals are supplied and correct.
+
+ Normals are not calculated in raw triangle mode, and skipping of null
+ triangles is likewise not performed. See the section on
+ \l{raw-triangle-mode}{raw triangle mode}
+ in the class documentation above.
+
+ \sa addQuads(), operator>>()
+*/
+void QGLBuilder::addTriangles(const QGeometryData &triangles)
+{
+ if (triangles.count() < 3)
+ return;
+ if (triangles.indexCount() > 0)
+ {
+ // raw triangle mode
+ if (dptr->currentSection == 0)
+ newSection();
+ dptr->currentSection->appendGeometry(triangles);
+ dptr->currentSection->appendIndices(triangles.indices());
+ dptr->currentNode->setCount(dptr->currentNode->count() + triangles.indexCount());
+ }
+ else
+ {
+ QGeometryData t = triangles;
+ bool calcNormal = !t.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(t.count());
+ t.appendNormalArray(nm);
+ }
+ bool skip = false;
+ int k = 0;
+ for (int i = 0; i < t.count() - 2; i += 3)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i, i+1, i+2, t);
+ if (!skip)
+ dptr->addTriangle(i, i+1, i+2, t, k);
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+ }
+}
+
+/*!
+ Add \a quads - a series of one or more quads - to this builder.
+
+ If \a quads has less than four vertices this function exits without
+ doing anything.
+
+ One normal per quad is calculated if \a quads does not have normals.
+ For this reason quads should have all four vertices in the same plane.
+ If the vertices do not lie in the same plane, use addTriangleStrip()
+ to add two adjacent triangles instead.
+
+ Since internally \l{geometry-building}{quads are stored as two triangles},
+ each quad is actually divided in half into two triangles.
+
+ Degenerate triangles are skipped in the same way as addTriangles().
+
+ \sa addTriangles(), addTriangleStrip()
+*/
+void QGLBuilder::addQuads(const QGeometryData &quads)
+{
+ if (quads.count() < 4)
+ return;
+ QGeometryData q = quads;
+ bool calcNormal = !q.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(q.count());
+ q.appendNormalArray(nm);
+ }
+ bool skip = false;
+ int k = 0;
+ QVector3D norm;
+ for (int i = 0; i < q.count(); i += 4)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i, i+1, i+2, q, &norm);
+ if (!skip)
+ dptr->addTriangle(i, i+1, i+2, q, k);
+ if (skip)
+ skip = qCalculateNormal(i, i+2, i+3, q, &norm);
+ if (!skip)
+ {
+ if (calcNormal)
+ setNormals(i, i+2, i+3, q, norm);
+ dptr->addTriangle(i, i+2, i+3, q, k);
+ }
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ Adds to this section a set of connected triangles defined by \a fan.
+
+ N triangular faces are generated, where \c{N == fan.count() - 2}. Each
+ face contains the 0th vertex in \a fan, followed by the i'th and i+1'th
+ vertex - where i takes on the values from 1 to \c{fan.count() - 1}.
+
+ If \a fan has less than three vertices this function exits without
+ doing anything.
+
+ This function is similar to the OpenGL mode GL_TRIANGLE_FAN. It
+ generates a number of triangles all sharing one common vertex, which
+ is the 0'th vertex of the \a fan.
+
+ Normals are calculated as for addTriangle(), given the above ordering.
+ There is no requirement or assumption that all triangles lie in the
+ same plane. Degenerate triangles are skipped in the same way as
+ addTriangles().
+
+ \sa addTriangulatedFace()
+*/
+void QGLBuilder::addTriangleFan(const QGeometryData &fan)
+{
+ if (fan.count() < 3)
+ return;
+ QGeometryData f = fan;
+ bool calcNormal = !f.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(f.count());
+ f.appendNormalArray(nm);
+ }
+ int k = 0;
+ bool skip = false;
+ for (int i = 1; i < f.count() - 1; ++i)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(0, i, i+1, f);
+ if (!skip)
+ dptr->addTriangle(0, i, i+1, f, k);
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ Adds to this section a set of connected triangles defined by \a strip.
+
+ N triangular faces are generated, where \c{N == strip.count() - 2}.
+ The triangles are generated from vertices 0, 1, & 2, then 2, 1 & 3,
+ then 2, 3 & 4, and so on. In other words every second triangle has
+ the first and second vertices switched, as a new triangle is generated
+ from each successive set of three vertices.
+
+ If \a strip has less than three vertices this function exits without
+ doing anything.
+
+ Normals are calculated as for addTriangle(), given the above ordering.
+
+ This function is very similar to the OpenGL mode GL_TRIANGLE_STRIP. It
+ generates triangles along a strip whose two sides are the even and odd
+ vertices.
+
+ \sa addTriangulatedFace()
+*/
+void QGLBuilder::addTriangleStrip(const QGeometryData &strip)
+{
+ if (strip.count() < 3)
+ return;
+ QGeometryData s = strip;
+ bool calcNormal = !s.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(s.count());
+ s.appendNormalArray(nm);
+ }
+ bool skip = false;
+ int k = 0;
+ for (int i = 0; i < s.count() - 2; ++i)
+ {
+ if (i % 2)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i+1, i, i+2, s);
+ if (!skip)
+ dptr->addTriangle(i+1, i, i+2, s, k);
+ }
+ else
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i, i+1, i+2, s);
+ if (!skip)
+ dptr->addTriangle(i, i+1, i+2, s, k);
+ }
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ Adds to this section a set of quads defined by \a strip.
+
+ If \a strip has less than four vertices this function exits without
+ doing anything.
+
+ The first quad is formed from the 0'th, 2'nd, 3'rd and 1'st vertices.
+ The second quad is formed from the 2'nd, 4'th, 5'th and 3'rd vertices,
+ and so on, as shown in this diagram:
+
+ \image quads.png
+
+ One normal per quad is calculated if \a strip does not have normals.
+ For this reason quads should have all four vertices in the same plane.
+ If the vertices do not lie in the same plane, use addTriangles() instead.
+
+ Since internally \l{geometry-building}{quads are stored as two triangles},
+ each quad is actually divided in half into two triangles.
+
+ Degenerate triangles are skipped in the same way as addTriangles().
+
+ \sa addQuads(), addTriangleStrip()
+*/
+void QGLBuilder::addQuadStrip(const QGeometryData &strip)
+{
+ if (strip.count() < 4)
+ return;
+ QGeometryData s = strip;
+ bool calcNormal = !s.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(s.count());
+ s.appendNormalArray(nm);
+ }
+ bool skip = false;
+ QVector3D norm;
+ int k = 0;
+ for (int i = 0; i < s.count() - 3; i += 2)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i, i+2, i+3, s, &norm);
+ if (!skip)
+ dptr->addTriangle(i, i+2, i+3, s, k);
+ if (skip)
+ skip = qCalculateNormal(i, i+3, i+1, s, &norm);
+ if (!skip)
+ {
+ if (calcNormal)
+ setNormals(i, i+3, i+1, s, norm);
+ dptr->addTriangle(i, i+3, i+1, s, k);
+ }
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ Adds to this section a polygonal face made of triangular sub-faces,
+ defined by \a face. The 0'th vertex is used for the center, while
+ the subsequent vertices form the perimeter of the face, which must
+ at minimum be a triangle.
+
+ If \a face has less than four vertices this function exits without
+ doing anything.
+
+ This function provides functionality similar to the OpenGL mode GL_POLYGON,
+ except it divides the face into sub-faces around a \bold{central point}.
+ The center and perimeter vertices must lie in the same plane (unlike
+ triangle fan). If they do not normals will be incorrectly calculated.
+
+ \image triangulated-face.png
+
+ Here the sub-faces are shown divided by green lines. Note how this
+ function handles some re-entrant (non-convex) polygons, whereas
+ addTriangleFan will not support such polygons.
+
+ If required, the center point can be calculated using the center() function
+ of QGeometryData:
+
+ \code
+ QGeometryData face;
+ face.appendVertex(perimeter.center()); // perimeter is a QGeometryData
+ face.appendVertices(perimeter);
+ builder.addTriangulatedFace(face);
+ \endcode
+
+ N sub-faces are generated where \c{N == face.count() - 2}.
+
+ Each triangular sub-face consists of the center; followed by the \c{i'th}
+ and \c{((i + 1) % N)'th} vertex. The last face generated then is
+ \c{(center, face[N - 1], face[0]}, the closing face. Note that the closing
+ face is automatically created, unlike addTriangleFan().
+
+ If no normals are supplied in the vertices of \a face, normals are
+ calculated as per addTriangle(). One normal is calculated, since a
+ faces vertices lie in the same plane.
+
+ Degenerate triangles are skipped in the same way as addTriangles().
+
+ \sa addTriangleFan(), addTriangles()
+*/
+void QGLBuilder::addTriangulatedFace(const QGeometryData &face)
+{
+ if (face.count() < 4)
+ return;
+ QGeometryData f;
+ f.appendGeometry(face);
+ int cnt = f.count();
+ bool calcNormal = !f.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(cnt);
+ f.appendNormalArray(nm);
+ }
+ bool skip = false;
+ QVector3D norm;
+ int k = 0;
+ for (int i = 1; i < cnt; ++i)
+ {
+ int n = i + 1;
+ if (n == cnt)
+ n = 1;
+ if (calcNormal)
+ {
+ skip = qCalculateNormal(0, i, n, f);
+ if (norm.isNull() && !skip)
+ {
+ norm = f.normalAt(0);
+ for (int i = 0; i < cnt; ++i)
+ f.normal(i) = norm;
+ }
+ }
+ if (!skip)
+ dptr->addTriangle(0, i, n, f, k);
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ Add a series of quads by 'interleaving' \a top and \a bottom.
+
+ This function behaves like quadStrip(), where the odd-numbered vertices in
+ the input primitive are from \a top and the even-numbered vertices from
+ \a bottom.
+
+ It is trivial to do extrusions using this function:
+
+ \code
+ // create a series of quads for an extruded edge along -Y
+ addQuadsInterleaved(topEdge, topEdge.translated(QVector3D(0, -1, 0));
+ \endcode
+
+ N quad faces are generated where \c{N == min(top.count(), bottom.count() - 1}.
+ If \a top or \a bottom has less than 2 elements, this functions does
+ nothing.
+
+ Each face is formed by the \c{i'th} and \c{(i + 1)'th}
+ vertices of \a bottom, followed by the \c{(i + 1)'th} and \c{i'th}
+ vertices of \a top.
+
+ If the vertices in \a top and \a bottom are the perimeter vertices of
+ two polygons then this function can be used to generate quads which form
+ the sides of a \l{http://en.wikipedia.org/wiki/Prism_(geometry)}{prism}
+ with the polygons as the prisms top and bottom end-faces.
+
+ \image quad-extrude.png
+
+ In the diagram above, the \a top is shown in orange, and the \a bottom in
+ dark yellow. The first generated quad, (a, b, c, d) is generated in
+ the order shown by the blue arrow.
+
+ To create such a extruded prismatic solid, complete with top and bottom cap
+ polygons, given just the top edge do this:
+ \code
+ QGeometryData top = buildTopEdge();
+ QGeometryData bottom = top.translated(QVector3D(0, 0, -1));
+ builder.addQuadsInterleaved(top, bottom);
+ builder.addTriangulatedFace(top);
+ builder.addTriangulatedFace(bottom.reversed());
+ \endcode
+ The \a bottom QGeometryData must be \bold{reversed} so that the correct
+ winding for an outward facing polygon is obtained.
+*/
+void QGLBuilder::addQuadsInterleaved(const QGeometryData &top,
+ const QGeometryData &bottom)
+{
+ if (top.count() < 2 || bottom.count() < 2)
+ return;
+ QGeometryData zipped = bottom.interleavedWith(top);
+ bool calcNormal = !zipped.hasField(QGL::Normal);
+ if (calcNormal)
+ {
+ QVector3DArray nm(zipped.count());
+ zipped.appendNormalArray(nm);
+ }
+ bool skip = false;
+ QVector3D norm;
+ int k = 0;
+ for (int i = 0; i < zipped.count() - 2; i += 2)
+ {
+ if (calcNormal)
+ skip = qCalculateNormal(i, i+2, i+3, zipped, &norm);
+ if (!skip)
+ dptr->addTriangle(i, i+2, i+3, zipped, k);
+ if (skip)
+ skip = qCalculateNormal(i, i+3, i+1, zipped, &norm);
+ if (!skip)
+ {
+ if (calcNormal)
+ setNormals(i, i+3, i+1, zipped, norm);
+ dptr->addTriangle(i, i+3, i+1, zipped, k);
+ }
+ }
+ dptr->currentNode->setCount(dptr->currentNode->count() + k);
+}
+
+/*!
+ \fn void QGLBuilder::addPane(QSizeF size)
+ Convenience function to create a quad centered on the origin,
+ lying in the Z=0 plane, with width (x dimension) and height
+ (y dimension) specified by \a size.
+*/
+
+/*!
+ \fn void QGLBuilder::addPane(qreal size)
+ Convenience method to add a single quad of dimensions \a size wide by
+ \a size high in the z = 0 plane, centered on the origin. The quad has
+ texture coordinates of (0, 0) at the bottom left and (1, 1) at the top
+ right. The default value for \a size is 1.0, resulting in a quad
+ from QVector3D(-0.5, -0.5, 0.0) to QVector3D(0.5, 0.5, 0.0).
+*/
+
+/*!
+ \internal
+*/
+void QGLBuilderPrivate::adjustSectionNodes(QGLSection *sec,
+ int offset, const QGeometryData &geom)
+{
+ QList<QGLSceneNode*> children = sec->nodes();
+ QList<QGLSceneNode*>::iterator it = children.begin();
+ QList<QGLSceneNode*> deleted;
+ for ( ; it != children.end(); ++it)
+ adjustNodeTree(*it, offset, geom, deleted);
+}
+
+/*!
+ \internal
+ Adjust \a top by incrementing its start by \a offset, and setting its
+ geometry to \a geom. Find the cumulative total of indexes -
+ QGLSceneNode::count() - for \a top and all its children. If this total is
+ equal to zero, then delete that node.
+*/
+int QGLBuilderPrivate::adjustNodeTree(QGLSceneNode *top,
+ int offset, const QGeometryData &geom,
+ QList<QGLSceneNode*> &deleted)
+{
+ int totalItems = 0;
+ if (top && !deleted.contains(top))
+ {
+ top->setStart(top->start() + offset);
+ top->setGeometry(geom);
+ totalItems = top->count();
+ QList<QGLSceneNode*> children = top->children();
+ QList<QGLSceneNode*>::iterator it = children.begin();
+ for ( ; it != children.end(); ++it)
+ {
+ totalItems += adjustNodeTree(*it, offset, geom, deleted);
+ }
+ if (totalItems == 0 && top->objectName().isEmpty())
+ {
+ delete top;
+ deleted.append(top);
+ }
+ }
+ return totalItems;
+}
+
+/*!
+ \internal
+ Returns a count of all the items referenced by this node
+ and all its children.
+*/
+static int recursiveCount(QGLSceneNode *top)
+{
+ int totalItems = 0;
+ if (top)
+ {
+ totalItems = top->count();
+ QList<QGLSceneNode*> children = top->children();
+ QList<QGLSceneNode*>::const_iterator it = children.constBegin();
+ for ( ; it != children.constEnd(); ++it)
+ totalItems += recursiveCount(*it);
+ }
+ return totalItems;
+}
+
+static int nodeCount(const QList<QGLSceneNode*> &list)
+{
+ int total = 0;
+ QList<QGLSceneNode*>::const_iterator it = list.constBegin();
+ for ( ; it != list.constEnd(); ++it)
+ total += recursiveCount(*it);
+ return total;
+}
+
+static inline void warnIgnore(int secCount, QGLSection *s, int vertCount, int nodeCount,
+ const char *msg)
+{
+ qWarning("Ignoring section %d (%p) with %d vertices and"
+ " %d indexes - %s", secCount, s, vertCount, nodeCount, msg);
+}
+
+/*!
+ Finish the building of this geometry, optimize it for rendering, and return a
+ pointer to the detached top-level scene node (root node).
+
+ Since the scene is detached from the builder object, the builder itself
+ may be deleted or go out of scope while the scene lives on:
+
+ \code
+ void MyView::MyView()
+ {
+ QGLBuilder builder;
+ // construct geometry
+ m_thing = builder.finalizedSceneNode();
+ }
+
+ void MyView::~MyView()
+ {
+ delete m_thing;
+ }
+
+ void MyView::paintGL()
+ {
+ m_thing->draw(painter);
+ }
+ \endcode
+
+ The root node will have a child node for each section that was created
+ during geometry building.
+
+ This method must be called exactly once after building the scene.
+
+ \bold{Calling code takes ownership of the scene.} In particular take care
+ to either explicitly destroy the scene when it is no longer needed - as shown
+ above.
+
+ For more complex applications parent each finalized scene node onto a QObject
+ so it will be implictly cleaned up by Qt. If you use QGLSceneNode::setParent()
+ to do this, you can save an explicit call to addNode() since if setParent()
+ detects that the new parent is a QGLSceneNode it will call addNode() for you:
+
+ \code
+ // here a top level node for the app is created, and parented to the view
+ QGLSceneNode *topNode = new QGLSceneNode(this);
+
+ QGLBuilder b1;
+ // build geometry
+
+ QGLSceneNode *thing = b1.finalizedSceneNode();
+
+ // does a QObject::setParent() to manage memory, and also adds to the scene
+ // graph, so no need to call topNode->addNode(thing)
+ thing->setParent(topNode);
+
+ QGLBuilder b2;
+ // build more geometry
+ QGLSceneNode *anotherThing = b2.finalizedSceneNode();
+
+ // again parent on get addNode for free
+ anotherThing->setParent(topNode);
+ \endcode
+
+ If this builder is destroyed without calling this method to take
+ ownership of the scene, a warning will be printed on the console and the
+ scene will be deleted. If this method is called more than once, on the
+ second and subsequent calls a warning is printed and NULL is returned.
+
+ This function does the following:
+ \list
+ \o packs all geometry data from sections into QGLSceneNode instances
+ \o recalculates QGLSceneNode start() and count() for the scene
+ \o deletes all QGLBuilder's internal data structures
+ \o returns the top level scene node that references the geometry
+ \o sets the internal pointer to the top level scene node to NULL
+ \endlist
+
+ \sa sceneNode()
+*/
+QGLSceneNode *QGLBuilder::finalizedSceneNode()
+{
+ if (dptr->rootNode == 0)
+ {
+ qWarning("QGLBuilder::finalizedSceneNode() called twice");
+ return 0;
+ }
+ QGeometryData g;
+ QMap<quint32, QGeometryData> geos;
+ QMap<QGLSection*, int> offsets;
+ for (int i = 0; i < dptr->sections.count(); ++i)
+ {
+ // pack sections that have the same fields into one geometry
+ QGLSection *s = dptr->sections.at(i);
+ QGL::IndexArray indices = s->indices();
+ int icnt = indices.size();
+ int ncnt = nodeCount(s->nodes());
+ int scnt = s->count();
+ if (scnt == 0 || icnt == 0 || ncnt == 0)
+ {
+ if (!qgetenv("Q_WARN_EMPTY_MESH").isEmpty())
+ {
+ if (ncnt == 0)
+ warnIgnore(scnt, s, icnt, ncnt, "nodes empty");
+ else if (scnt == 0)
+ warnIgnore(scnt, s, icnt, ncnt, "geometry count zero");
+ else
+ warnIgnore(scnt, s, icnt, ncnt, "index count zero");
+ }
+ continue;
+ }
+ s->normalizeNormals();
+ int sectionOffset = 0;
+ int sectionIndexOffset = 0;
+ if (geos.contains(s->fields()))
+ {
+ QGeometryData &gd = geos[s->fields()];
+ sectionOffset = gd.count();
+ sectionIndexOffset = gd.indexCount();
+ offsets.insert(s, sectionIndexOffset);
+ gd.appendGeometry(*s);
+ for (int i = 0; i < icnt; ++i)
+ indices[i] += sectionOffset;
+ gd.appendIndices(indices);
+ }
+ else
+ {
+ g = QGeometryData(*s);
+ geos.insert(s->fields(), g);
+ }
+ }
+ while (dptr->sections.count() > 0)
+ {
+ QGLSection *s = dptr->sections.takeFirst();
+ dptr->adjustSectionNodes(s, offsets[s], geos[s->fields()]);
+ delete s;
+ }
+ QGLSceneNode *tmp = dptr->rootNode;
+ dptr->rootNode = 0; // indicates root node detached
+ return tmp;
+}
+
+/*!
+ Creates a new section with smoothing mode set to \a smooth. By default
+ \a smooth is QGL::Smooth.
+
+ A section must be created before any geometry or new nodes can be added
+ to the builder. However one is created automatically by addTriangle()
+ and the other add functions; and also by newNode(), pushNode() or popNode()
+ if needed.
+
+ The internal node stack - see pushNode() and popNode() - is cleared,
+ and a new top-level QGLSceneNode is created for this section by calling
+ newNode().
+
+ \sa newNode(), pushNode()
+*/
+void QGLBuilder::newSection(QGL::Smoothing smooth)
+{
+ new QGLSection(this, smooth); // calls addSection
+}
+
+void QGLBuilder::addSection(QGLSection *sec)
+{
+ dptr->currentSection = sec;
+ sec->setMapThreshold(dptr->defThreshold);
+ dptr->sections.append(sec);
+ dptr->nodeStack.clear();
+ newNode();
+}
+
+/*!
+ \internal
+ Returns the current section, in which new geometry is being added.
+*/
+QGLSection *QGLBuilder::currentSection() const
+{
+ return dptr->currentSection;
+}
+
+/*!
+ \internal
+ Returns a list of the sections of the geometry in this builder.
+*/
+QList<QGLSection*> QGLBuilder::sections() const
+{
+ return dptr->sections;
+}
+
+/*!
+ \internal
+ Test function only.
+*/
+void QGLBuilder::setDefaultThreshold(int t)
+{
+ dptr->defThreshold = t;
+}
+
+/*!
+ Returns the root scene node of the geometry created by this builder.
+
+ \sa newNode(), newSection()
+*/
+QGLSceneNode *QGLBuilder::sceneNode()
+{
+ return dptr->rootNode;
+}
+
+/*!
+ Creates a new QGLSceneNode and makes it current. A pointer to the new
+ node is returned. The node is added into the scene at the same level
+ as the currentNode().
+
+ The node is set to reference the geometry starting from the next
+ vertex created, such that currentNode()->start() will return the
+ index of this next vertex.
+
+ \sa newSection()
+*/
+QGLSceneNode *QGLBuilder::newNode()
+{
+ if (dptr->currentSection == 0)
+ {
+ newSection(); // calls newNode()
+ return dptr->currentNode;
+ }
+ QGLSceneNode *parentNode = dptr->rootNode;
+ if (dptr->nodeStack.count() > 0)
+ parentNode = dptr->nodeStack.last();
+ dptr->currentNode = new QGLSceneNode(parentNode);
+ dptr->currentNode->setPalette(parentNode->palette());
+ dptr->currentNode->setStart(dptr->currentSection->indexCount());
+ if (dptr->nodeStack.count() == 0)
+ dptr->currentSection->addNode(dptr->currentNode);
+ return dptr->currentNode;
+}
+
+/*!
+ Returns a pointer to the current scene node, within the current section.
+
+ If there is no current section then newSection() will be called to
+ create one.
+
+ \sa newNode(), newSection()
+*/
+QGLSceneNode *QGLBuilder::currentNode()
+{
+ if (dptr->currentSection == 0)
+ newSection(); // calls newNode()
+ return dptr->currentNode;
+}
+
+/*!
+ Creates a new scene node that is a child of the current node and,
+ makes it the current node. A pointer to the new node is returned.
+ The previous current node is saved on a stack and it may
+ be made current again by calling popNode().
+
+ \sa popNode(), newNode()
+*/
+QGLSceneNode *QGLBuilder::pushNode()
+{
+ if (dptr->currentSection == 0)
+ newSection(); // calls newNode()
+ QGLSceneNode *parentNode = dptr->currentNode;
+ dptr->nodeStack.append(parentNode);
+ dptr->currentNode = new QGLSceneNode(parentNode);
+ dptr->currentNode->setStart(dptr->currentSection->indexCount());
+ dptr->currentNode->setPalette(parentNode->palette());
+ return dptr->currentNode;
+}
+
+/*!
+ Removes the node from the top of the stack, makes a copy of it, and
+ makes the copy current.
+
+ If the stack is empty, behaviour is undefined. In debug mode, calling
+ this function when the stack is empty will cause an assert.
+
+ A pointer to the new current node is returned.
+
+ The node is set to reference the geometry starting from the next
+ vertex created, such that QGLSceneNode::start() will return the
+ index of this next vertex.
+
+ \sa pushNode(), newNode()
+*/
+QGLSceneNode *QGLBuilder::popNode()
+{
+ if (dptr->currentSection == 0)
+ newSection(); // calls newNode()
+ int cnt = dptr->currentSection->indexCount();
+ QGLSceneNode *s = dptr->nodeStack.takeLast(); // assert here
+ QGLSceneNode *parentNode = dptr->rootNode;
+ if (dptr->nodeStack.count() > 0)
+ parentNode = dptr->nodeStack.last();
+ dptr->currentNode = s->cloneNoChildren(parentNode);
+ dptr->currentNode->setStart(cnt);
+ dptr->currentNode->setCount(0);
+ dptr->currentNode->setPalette(parentNode->palette());
+ if (dptr->nodeStack.count() == 0)
+ dptr->currentSection->addNode(dptr->currentNode);
+ return dptr->currentNode;
+}
+
+/*!
+ Returns the palette for this builder. This is the QGLMaterialCollection
+ pointer that was passed to the constructor; or if that was null a new
+ QGLMaterialCollection. This function returns the same result as
+ \c{sceneNode()->palette()}.
+
+ \sa sceneNode()
+*/
+QGLMaterialCollection *QGLBuilder::palette()
+{
+ return dptr->rootNode->palette();
+}
+
+/*!
+ \relates QGLBuilder
+ Convenience operator for creating a new section in \a builder with \a smoothing.
+
+ \code
+ // equivalent to builder.newSection(QGL::Faceted)
+ builder << QGL::Faceted;
+ \endcode
+*/
+QGLBuilder& operator<<(QGLBuilder& builder, const QGL::Smoothing& smoothing)
+{
+ builder.newSection(smoothing);
+ return builder;
+}
+
+/*!
+ \relates QGLBuilder
+ Convenience operator for adding \a triangles to the \a builder.
+
+ \code
+ // equivalent to builder.addTriangles(triangles);
+ builder << triangles;
+ \endcode
+*/
+QGLBuilder& operator<<(QGLBuilder& builder, const QGeometryData& triangles)
+{
+ builder.addTriangles(triangles);
+ return builder;
+}
diff --git a/src/threed/geometry/qglbuilder.h b/src/threed/geometry/qglbuilder.h
new file mode 100644
index 000000000..8c278e192
--- /dev/null
+++ b/src/threed/geometry/qglbuilder.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLBuilder_H
+#define QGLBuilder_H
+
+#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
+#include <QtGui/qvector3d.h>
+#include <QtOpenGL/qgl.h>
+
+#include "qglnamespace.h"
+#include "qglscenenode.h"
+#include "qglattributevalue.h"
+#include "qgeometrydata.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLSection;
+class QGLMaterialCollection;
+class QGLBuilderPrivate;
+class QGLPainter;
+
+class Q_QT3D_EXPORT QGLBuilder
+{
+public:
+ explicit QGLBuilder(QGLMaterialCollection *materials = 0);
+ virtual ~QGLBuilder();
+
+ // section management
+ void newSection(QGL::Smoothing sm = QGL::Smooth);
+
+ // scene management
+ QGLSceneNode *sceneNode();
+ QGLSceneNode *currentNode();
+ QGLSceneNode *newNode();
+ QGLSceneNode *pushNode();
+ QGLSceneNode *popNode();
+ QGLMaterialCollection *palette();
+ QGLSceneNode *finalizedSceneNode();
+
+ // geometry building by primitive
+ void addTriangles(const QGeometryData &triangle);
+ void addQuads(const QGeometryData &quad);
+ void addTriangleFan(const QGeometryData &fan);
+ void addTriangleStrip(const QGeometryData &strip);
+ void addTriangulatedFace(const QGeometryData &face);
+ void addQuadStrip(const QGeometryData &strip);
+ void addQuadsInterleaved(const QGeometryData &top,
+ const QGeometryData &bottom);
+ inline void addPane(qreal size = 1.0f);
+ inline void addPane(QSizeF size);
+
+protected:
+ // internal and test functions
+ QGLSection *currentSection() const;
+ QList<QGLSection*> sections() const;
+ void setDefaultThreshold(int);
+
+private:
+ Q_DISABLE_COPY(QGLBuilder);
+ void addSection(QGLSection *section);
+
+ friend class QGLSection;
+
+ QGLBuilderPrivate *dptr;
+};
+
+inline void QGLBuilder::addPane(qreal size)
+{
+ addPane(QSizeF(size, size));
+}
+
+inline void QGLBuilder::addPane(QSizeF size)
+{
+ QSizeF f = size / 2.0f;
+ QVector2D a(-f.width(), -f.height());
+ QVector2D b(f.width(), -f.height());
+ QVector2D c(f.width(), f.height());
+ QVector2D d(-f.width(), f.height());
+ QVector2D ta(0.0f, 0.0f);
+ QVector2D tb(1.0f, 0.0f);
+ QVector2D tc(1.0f, 1.0f);
+ QVector2D td(0.0f, 1.0f);
+ QGeometryData quad;
+ quad.appendVertex(a, b, c, d);
+ quad.appendTexCoord(ta, tb, tc, td);
+ addQuads(quad);
+}
+
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGL::Smoothing& smoothing);
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGeometryData& triangles);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLBuilder_H
diff --git a/src/threed/geometry/qglbuilder_p.h b/src/threed/geometry/qglbuilder_p.h
new file mode 100644
index 000000000..bcbd16883
--- /dev/null
+++ b/src/threed/geometry/qglbuilder_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLBuilder_P_H
+#define QGLBuilder_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qglbuilder.h"
+
+#include <QtCore/qmap.h>
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QGLBuilder;
+class QGLSection;
+class QGeometryData;
+
+class QGLBuilderPrivate
+{
+public:
+ QGLBuilderPrivate(QGLBuilder *parent);
+ ~QGLBuilderPrivate();
+ inline void setDirty(bool dirty = true);
+ void addTriangle(int a, int b, int c, const QGeometryData &p, int &count);
+ void adjustSectionNodes(QGLSection *sec, int offset, const QGeometryData &geom);
+ int adjustNodeTree(QGLSceneNode *top, int offset, const QGeometryData &geom,
+ QList<QGLSceneNode*> &deleted);
+
+ QList<QGLSection*> sections;
+ QGLSection *currentSection;
+ QList<QGLSceneNode*> nodeStack;
+ QGLSceneNode *currentNode;
+ QGLSceneNode *rootNode;
+ int defThreshold;
+ QGLBuilder *q;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGLBuilder_P_H
diff --git a/src/threed/geometry/qglcube.cpp b/src/threed/geometry/qglcube.cpp
new file mode 100644
index 000000000..5f5bda118
--- /dev/null
+++ b/src/threed/geometry/qglcube.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglcube.h"
+#include "qglbuilder.h"
+#include "qvector3darray.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLCube
+ \brief The QGLCube class represents the geometry of simple six-sided cube in 3D space.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The following example adds a cube of 2 units on a side to a
+ geometry builder, and draws it at (10, 25, 0) in a QGLPainter:
+
+ \code
+ QGLBuilder list;
+ list.newSection(QGL::Faceted);
+ list << QGLCube(2);
+ painter->translate(10, 25, 0);
+ list.draw(painter);
+ \endcode
+
+ QGLCube will create a default set of texture coordinates that shows
+ the same texture of all six faces.
+*/
+
+/*!
+ \fn QGLCube::QGLCube(qreal size)
+
+ Constructs the geometry for a regular cube of \a size
+ units on a side.
+*/
+
+/*!
+ \fn qreal QGLCube::size() const
+
+ Returns the size of this cube.
+
+ \sa setSize()
+*/
+
+/*!
+ \fn void QGLCube::setSize(qreal size)
+
+ Sets the \a size of this cube.
+
+ \sa size()
+*/
+
+static const int vertexDataLen = 6 * 4 * 3;
+
+static const float vertexData[vertexDataLen] = {
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, -0.5f,
+
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f,
+
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, -0.5f,
+
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, 0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, -0.5f, -0.5f,
+
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, -0.5f, 0.5f,
+
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f
+};
+
+static const int texCoordDataLen = 4 * 2;
+
+static const float texCoordData[texCoordDataLen] = {
+ 1.0f, 0.0f,
+ 1.0f, 1.0f,
+ 0.0f, 1.0f,
+ 0.0f, 0.0f
+};
+
+/*!
+ \relates QGLCube
+
+ Builds the geometry for \a cube within the specified
+ geometry \a builder.
+
+ This operator specifies the positions, and 2D texture
+ co-ordinates for all of the vertices that make up the cube.
+ Normals will be calculated by the \a builder, depending on its
+ current section's smoothing setting.
+*/
+QGLBuilder& operator<<(QGLBuilder& builder, const QGLCube& cube)
+{
+ QGeometryData op;
+
+ QVector3DArray vrts = QVector3DArray::fromRawData(
+ reinterpret_cast<const QVector3D *>(vertexData), vertexDataLen / 3);
+ if (cube.size() != 1.0f)
+ vrts.scale(cube.size());
+
+ op.appendVertexArray(vrts);
+
+ QVector2DArray texx = QVector2DArray::fromRawData(
+ reinterpret_cast<const QVector2D *>(texCoordData), texCoordDataLen / 2);
+
+ for (int i = 0; i < 6; ++i)
+ op.appendTexCoordArray(texx);
+
+ builder.addQuads(op);
+ return builder;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/geometry/qglcube.h b/src/threed/geometry/qglcube.h
new file mode 100644
index 000000000..19eebc26c
--- /dev/null
+++ b/src/threed/geometry/qglcube.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLCUBE_H
+#define QGLCUBE_H
+
+#include "qt3dglobal.h"
+
+#include <QtGui/qvector2d.h>
+#include "qvector2darray.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLBuilder;
+
+class Q_QT3D_EXPORT QGLCube
+{
+public:
+ explicit QGLCube(qreal size = 1.0f) : m_size(size) {}
+
+ qreal size() const { return m_size; }
+ void setSize(qreal size) { m_size = size; }
+
+private:
+ qreal m_size;
+};
+
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLCube& cube);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/geometry/qglcylinder.cpp b/src/threed/geometry/qglcylinder.cpp
new file mode 100644
index 000000000..30b4f34cc
--- /dev/null
+++ b/src/threed/geometry/qglcylinder.cpp
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglcylinder.h"
+#include "qglbuilder.h"
+#include "qvector2darray.h"
+#include "qvector3darray.h"
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLCylinder
+ \brief The QGLCylinder class represents the geometry of a simple cylinder/cone in 3D space.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The following example creates a cone with a top diameter of 1 unit,
+ a bottom diameter of of 2 units in diameter and height of 3 units.
+
+ It then draws it at (10, 25, 0) in a QGLPainter:
+
+ \code
+ QGLBuilder builder;
+ builder << QGLCylinder(1.0,2.0,3.0);
+ QGLSceneNode *node = builder.finalizedSceneNode();
+
+ painter.translate(10, 25, 0);
+ node->draw(&painter);
+ \endcode
+
+ Note that the bottom circle of the cylinder will always be centred at (0,0,0)
+ unless otherwise transformed after cylinder creation.
+
+ The QGLCylinder class specifies positions, normals and 2D texture
+ co-ordinates for all of the vertices that make up the cylinder.
+
+ The texture co-ordinates are fixed at construction time. This
+ is because constructing the cylinder can involve generating additional
+ vertices which need to interpolate the texture co-ordinates of their
+ neighboring vertices.
+
+ The QGLCylinder is divided into slices and layers. The slices value
+ indicate number of triangular sections into which the top and bottom
+ circles of the cylinder are broken into. Consequently it also sets the
+ number of facets which run the length of the cylinder. More slices
+ results in a smoother circumference.
+
+ The layers value indicates the number of longitudinal sections the
+ cylinder is broken into. Fewer layers means that the side facets of the
+ cylinder will be made up of fewer, very long, triangles, while a higher
+ number of layers will produce many and smaller triangles. Often it is
+ desirable to avoid large triangles as they may cause inefficiencies in
+ texturing/lighting on certain platforms.
+
+ The end-caps and sides of the cylinder are independent sections of the
+ scene-graph, and so may be textured separately.
+
+ Textures are wrapped around the sides of thecylinder in such a way that
+ the texture may distort across the x axis if the top and bottom diameters
+ of the cylinder differ (ie. the cylinder forms a truncated cone). Textures
+ begin and end at the centre points of the top and bottom end-caps of the
+ cylinder. This wrapping means that textures on either end-cap may be
+ distorted.
+
+ Texture coordinates are assigned as shown below.
+
+ \image cylinder-texture-coords.png
+
+ It is worth noting that the cylinder class can, in fact, be used to generate
+ any regular solid polygonal prism. A rectangular prism can be created, for
+ example, by creating a 4 sided cylinder. Likewise a hexagonal prism is
+ simply a 6 sided cylinder.
+
+ With this knowledge, and an understanding of the texture coordinate mapping,
+ it is possible to make custom textures which will be usable with these
+ three dimensional objects.
+
+ \sa QGLBuilder
+*/
+
+
+/*!
+ \fn QGLCylinder::QGLCylinder(qreal diameterTop, qreal diameterBase , qreal height, int slices, int layers, bool top, bool base)
+
+ Constructs the geometry for a cylinder with top of diameter \a diameterTop,
+ a base of diameter \a diameterBase, and a height of \a height.
+
+ The resultant mesh will be divided around the vertical axis of the cylinder
+ into \a slices individual wedges, and shall be formed of \a layers stacked
+ to form the cylinder.
+
+ If the values for \a top or \a base are true, then the cylinder will be
+ created with solid endcaps. Otherwise, it shall form a hollow pipe.
+
+ units on a side.
+*/
+
+
+/*!
+ \fn qreal QGLCylinder::diameterTop() const
+
+ Returns the diameter of the top of the cylinder.
+
+ The default value is 1.
+
+ \sa setDiameterTop()
+*/
+
+/*!
+ \fn void QGLCylinder::setDiameterTop(qreal diameter)
+
+ Sets the diameter of the top of this cylinder to \a diameter.
+
+ \sa diameterTop()
+*/
+
+/*!
+ \fn qreal QGLCylinder::diameterBottom() const
+
+ Returns the diameter of the bottom of the cylinder.
+
+ The default value is 1.
+
+ \sa setDiameterBottom()
+*/
+
+/*!
+ \fn void QGLCylinder::setDiameterBottom(qreal diameter)
+
+ Sets the diameter of the bottom of this cylinder to \a diameter.
+
+ \sa diameterBottom()
+*/
+
+/*!
+ \fn qreal QGLCylinder::height() const
+
+ Returns the height of the cylinder.
+
+ The default value is 1.0
+
+ \sa setDiameterBottom()
+*/
+
+/*!
+ \fn void QGLCylinder::setHeight(qreal height)
+
+ Sets the height of this cylinder to \a height.
+
+ \sa diameterBottom()
+*/
+
+
+/*!
+ \fn int QGLCylinder::slices() const
+
+ Returns the number of triangular slices the cylinder is divided into
+ around its polar axis.
+
+ The default is 6.
+
+ \sa setSlices()
+*/
+
+/*!
+ \fn int QGLCylinder::setSlices(int slices)
+
+ Sets the number of triangular \a slices the cylinder is divided into
+ around its polar axis.
+
+ \sa slices()
+*/
+
+/*!
+ \fn int QGLCylinder::layers() const
+
+ Returns the number of cylindrical layers the cylinder is divided into
+ along its height.
+
+ The default is 3.
+
+ \sa setLayers()
+*/
+
+/*!
+ \fn int QGLCylinder::setLayers(int layers)
+
+ Sets the number of stacked \a layers the cylinder is divided into
+ along its height.
+
+ \sa layers()
+*/
+
+/*!
+ \fn bool QGLCylinder::topEnabled() const
+
+ Returns true if the top of the cyclinder will be created when
+ building the mesh.
+
+ The default is true.
+
+ \sa setTopEnabled()
+*/
+
+/*!
+ \fn void QGLCylinder::setTopEnabled(bool top)
+
+ Set whether the top end-cap of the cylinder will be created when
+ building the mesh. If \a top is true, the end-cap will be created.
+
+ \sa topEnabled()
+*/
+
+/*!
+ \fn bool QGLCylinder::baseEnabled() const
+
+ Returns true if the base of the cyclinder will be created when
+ building the mesh.
+
+ The default is true.
+
+ \sa setBaseEnabled()
+*/
+
+/*!
+ \fn void QGLCylinder::setBaseEnabled(bool base)
+
+ Set whether the base end-cap of the cylinder will be created when
+ building the mesh. If \a base is true, the end-cap will be created.
+
+ \sa baseEnabled()
+*/
+
+/*!
+ \relates QGLCylinder
+
+ Builds the geometry for \a cylinder within the specified
+ geometry \a builder.
+*/
+
+QGLBuilder& operator<<(QGLBuilder& builder, const QGLCylinder& cylinder)
+{
+ /* ASSERT(cylinder.diameterBottom()>=0 &&
+ cylinder.diameterTop()>=0 &&
+ cylinder.height()>0);*/
+
+ qreal numSlices = qreal(cylinder.slices());
+ qreal numLayers = qreal(cylinder.layers());
+ qreal topRadius = cylinder.diameterTop()/2.0;
+ qreal bottomRadius = cylinder.diameterBottom()/2.0;
+
+ qreal angle = 0;
+ qreal angleIncrement = (2.0 * M_PI) / numSlices;
+ qreal radius = topRadius;
+ qreal radiusIncrement = qreal(bottomRadius-topRadius)/ numLayers;
+ qreal height = qreal(cylinder.height());
+ qreal heightDecrement = height/numLayers;
+
+ qreal textureHeight = 1.0;
+ qreal textureDecrement = 1.0/numLayers;
+
+ QGeometryData oldLayer;
+
+ //Generate vertices for the next layer of cylinder
+ for (int layerCount=0; layerCount<=cylinder.layers(); layerCount++) {
+ QGeometryData newLayer;
+
+ //Generate a circle of vertices for this layer.
+ for (int i=0; i<cylinder.slices(); i++)
+ {
+ newLayer.appendVertex(QVector3D(radius * qCos(angle),
+ radius * qSin(angle),
+ height));
+ angle+=angleIncrement;
+ }
+ angle = 0;
+ // Generate texture coordinates (including an extra seam vertex for textures).
+ newLayer.appendVertex(newLayer.vertex(0));
+ newLayer.generateTextureCoordinates();
+ for (int i = 0; i < newLayer.count(); i++) newLayer.texCoord(i).setY(textureHeight);
+
+ //Special cases for top end-cap
+ if (layerCount==0 && cylinder.topEnabled()) {
+ //Draw end-cap at top
+ QGeometryData top;
+ builder.newSection();
+ builder.currentNode()->setObjectName("Cylinder Top");
+ top.appendVertex(newLayer.center());
+ top.appendVertexArray(newLayer.vertices());
+ //Generate a circle of texture vertices for this layer.
+ top.appendTexCoord(QVector2D(0.5,0.5));
+
+ for (int i=1; i<top.count(); i++)
+ {
+ top.appendTexCoord(QVector2D(0.5*qCos(angle)+0.5, 0.5*qSin(angle)+0.5));
+ angle+=angleIncrement;
+ }
+ angle = 0;
+ builder.addTriangulatedFace(top);
+ }
+
+
+ //Add a new cylinder layer to the mesh
+ if (layerCount>0)
+ {
+ //If it's the first section, create a cylinder sides section
+ if (layerCount==1) {
+ builder.newSection();
+ builder.currentNode()->setObjectName("Cylinder Sides");
+ }
+ builder.addQuadsInterleaved(oldLayer, newLayer);
+ }
+
+ //Special cases for bottom end-cap
+ if (layerCount==cylinder.layers() && cylinder.baseEnabled()) {
+ //Draw end-cap at bottom
+ QGeometryData base;
+ builder.newSection();
+ builder.currentNode()->setObjectName("Cylinder Base");
+ base.appendVertexArray(newLayer.vertices());
+ base.appendVertex(newLayer.center());
+ //Generate a circle of texture vertices for this layer.
+ for (int i=1; i<base.count(); i++)
+ {
+ base.appendTexCoord(QVector2D(0.5*qCos(angle)+0.5, 0.5*qSin(angle)+0.5));
+ angle+=angleIncrement;
+ }
+ base.appendTexCoord(QVector2D(0.5,0.5));
+ angle = 0;
+
+ //we need to reverse the above to draw it properly - windings!
+ builder.addTriangulatedFace(base.reversed());
+ }
+
+ //Keep the current layer for drawing the next segment of the cylinder
+ oldLayer.clear();
+ oldLayer.appendGeometry(newLayer);
+ radius+=radiusIncrement;
+ height-=heightDecrement;
+ textureHeight-=textureDecrement;
+ }
+
+ return builder;
+}
diff --git a/src/threed/geometry/qglcylinder.h b/src/threed/geometry/qglcylinder.h
new file mode 100644
index 000000000..51d2cfba6
--- /dev/null
+++ b/src/threed/geometry/qglcylinder.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLCYLINDER_H
+#define QGLCYLINDER_H
+
+#include "qt3dglobal.h"
+#include "qglmaterialcollection.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLBuilder;
+class QVector2D;
+
+class Q_QT3D_EXPORT QGLCylinder
+{
+public:
+ explicit QGLCylinder(qreal diameterTop = 1.0f, qreal diameterBase = 1.0f, qreal height = 1.0f, int slices = 6, int layers = 3, bool top = true, bool base = true)
+ : m_diameterTop(diameterTop), m_diameterBottom(diameterBase), m_height(height), m_slices(slices), m_layers(layers), m_top(top), m_base(base) {}
+
+ //Cylinder dimensions
+ qreal diameterTop() const {return m_diameterTop;}
+ void setDiameterTop(qreal diameter) {m_diameterTop=diameter;}
+
+ qreal diameterBottom() const {return m_diameterBottom;}
+ void setDiameterBottom(qreal diameter) {m_diameterBottom=diameter;}
+
+ qreal height() const {return m_height;}
+ void setHeight(qreal height) {m_height = height;}
+
+ //Cylinder geometrical subdivisions
+ int slices() const {return m_slices;}
+ void setSlices(int slices) {m_slices = slices;}
+
+ int layers() const {return m_layers;}
+ void setLayers(int layers) {m_layers = layers;}
+
+ //End-caps attached?
+ bool topEnabled() const {return m_top;}
+ void setTopEnabled(bool top) {m_top = top;}
+
+ bool baseEnabled() const {return m_base;}
+ void setBaseEnabled(bool base) {m_base = base;}
+
+protected:
+ qreal m_diameterTop;
+ qreal m_diameterBottom;
+ qreal m_height;
+
+ int m_slices;
+ int m_layers;
+
+ bool m_top;
+ bool m_base;
+};
+
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLCylinder& cylinder);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLCYLINDER_H
diff --git a/src/threed/geometry/qgldome.cpp b/src/threed/geometry/qgldome.cpp
new file mode 100644
index 000000000..5cd18da01
--- /dev/null
+++ b/src/threed/geometry/qgldome.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qgldome.h"
+#include "qglbuilder.h"
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLDome
+ \brief The QGLDome class represents the geometry of a simple hemisphere in 3D space.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The following example creates a dome of 2 units in diameter and
+ draws it at (10, 25, 0) in a QGLPainter:
+
+ \code
+ QGLBuilder builder;
+ builder << QGLDome(2);
+ QGLSceneNode *node = builder.finalizedSceneNode();
+
+ painter.translate(10, 25, 0);
+ node->draw(&painter);
+ \endcode
+
+ The QGLDome class specifies positions, normals and 2D texture
+ co-ordinates for all of the vertices that make up the sphere.
+
+ The texture co-ordinates are fixed at construction time. This
+ is because constructing the sphere can involve generating additional
+ vertices which need to interpolate the texture co-ordinates of their
+ neighboring vertices.
+
+ The default mode of QGLDome is half of a "UV sphere", which divides
+ the object up into longitudinal and latitudinal sections. The longitudinal
+ slices meet at the pole, which in a single unit dome is defined to
+ be at (0, 0, +0.5) and (0, 0, -0.5). This choice is the simplest to
+ texture map as the texture will only distort along the x-axis of the
+ 2D texture. However the density of vertices is significantly higher at
+ the poles than it is elsewhere on the sphere and is a poor choice if a
+ uniform density of pixels from the texture map is required.
+
+ \sa QGLBuilder
+*/
+
+/*!
+ \fn QGLDome::QGLDome(qreal diameter, int depth, bool base)
+
+ Creates a dome of \a diameter across (default is 1). When the dome
+ is recursively subdivided into triangles, it will be subdivided no more
+ than \a depth times (between 1 and 5, default is 3).
+
+ If \a base is true, the dome will be drawn with a bottom circle, creating
+ an enclosed solid.
+*/
+
+/*!
+ Destroys this dome object.
+*/
+QGLDome::~QGLDome()
+{
+}
+
+/*!
+ \fn qreal QGLDome::diameter() const
+
+ Returns the diameter of this dome. The default is 1.
+
+ \sa setDiameter()
+*/
+
+/*!
+ \fn void QGLDome::setDiameter(qreal diameter)
+
+ Sets the diameter of this dome to \a diameter.
+
+ \sa diameter()
+*/
+
+/*!
+ \fn int QGLDome::subdivisionDepth() const
+
+ Returns the maximum depth when this hemisphere is subdivided into
+ triangles. The default is 3. The following picture shows the effect
+ of depth values between 1 and 5 for a UV sphere (hemisphere subdivision
+ depth shares this scheme).
+
+ \image sphere-detail.png
+
+ \sa setSubdivisionDepth(), QGLSphere::subdivisionDepth()
+*/
+
+/*!
+ \fn void QGLDome::setSubdivisionDepth(int depth)
+
+ Sets the maximum \a depth when this hemisphere is subdivided into triangles.
+
+ \sa subdivisionDepth()
+*/
+
+/*!
+ \fn bool QGLDome::baseEnabled() const
+
+ Returns true if the base of the dome will be created when
+ building the mesh.
+
+ The default is true.
+
+ \sa setBaseEnabled()
+*/
+
+/*!
+ \fn void QGLDome::setBaseEnabled(bool base)
+
+ Set whether the bottom of the dome will be created when
+ building the mesh. If \a base is true, the end-cap will be
+ created.
+
+ \sa baseEnabled()
+*/
+
+/*!
+ \relates QGLDome
+
+ Builds the geometry for \a dome within the specified
+ geometry \a builder.
+*/
+QGLBuilder& operator<<(QGLBuilder& builder, const QGLDome& dome)
+{
+ qreal radius = dome.diameter() / 2.0f;
+
+ // Determine the number of slices and stacks to generate.
+ int divisions = dome.subdivisionDepth();
+ if (divisions < 1)
+ divisions = 1;
+ else if (divisions > 5)
+ divisions = 5;
+ int stacks = 2 * (1 << divisions);
+ int slices = 2 * stacks;
+ stacks = stacks>>1;
+
+ // Precompute sin/cos values for the slices and stacks.
+ const int maxSlices = 4 * (1 << 5) + 1;
+ const int maxStacks = 2 * (1 << 5) + 1;
+ qreal sliceSin[maxSlices];
+ qreal sliceCos[maxSlices];
+ qreal stackSin[maxStacks];
+ qreal stackCos[maxStacks];
+ for (int slice = 0; slice < slices; ++slice) {
+ qreal angle = 2 * M_PI * slice / slices;
+ sliceSin[slice] = qFastSin(angle);
+ sliceCos[slice] = qFastCos(angle);
+ }
+ sliceSin[slices] = sliceSin[0]; // Join first and last slice.
+ sliceCos[slices] = sliceCos[0];
+
+ const qreal halfPi=M_PI/2.0;
+
+ for (int stack = 0; stack <= stacks; ++stack) {
+ qreal angle = halfPi * stack / stacks;
+ stackSin[stack] = qFastSin(angle);
+ stackCos[stack] = qFastCos(angle);
+ }
+ stackSin[0] = 0.0f; // Come to a point at the poles.
+ stackSin[stacks] = 1.0f;
+
+ builder.newSection();
+ builder.currentNode()->setObjectName("Dome");
+ // Create the stacks for the dome part of the dome
+ for (int stack = 0; stack < stacks; ++stack) {
+ QGeometryData prim;
+ qreal z = radius * stackCos[stack];
+ qreal nextz = radius * stackCos[stack + 1];
+ qreal s = stackSin[stack];
+ qreal nexts = stackSin[stack + 1];
+ qreal c = stackCos[stack];
+ qreal nextc = stackCos[stack + 1];
+ qreal r = radius * s;
+ qreal nextr = radius * nexts;
+ for (int slice = 0; slice <= slices; ++slice) {
+ prim.appendVertex(QVector3D(nextr * sliceSin[slice], nextr * sliceCos[slice], nextz));
+ prim.appendNormal(QVector3D(sliceSin[slice] * nexts, sliceCos[slice] * nexts, nextc));
+ prim.appendTexCoord(QVector2D(1.0f - qreal(slice) / slices, 1.0f - qreal(stack + 1) / stacks));
+
+ prim.appendVertex(QVector3D(r * sliceSin[slice], r * sliceCos[slice], z));
+ prim.appendNormal(QVector3D(sliceSin[slice] * s, sliceCos[slice] * s, c));
+ prim.appendTexCoord(QVector2D(1.0f - qreal(slice) / slices, 1.0f - qreal(stack) / stacks));
+ }
+ builder.addQuadStrip(prim);
+ }
+
+ if (dome.baseEnabled()) {
+ //Draw end-cap at bottom
+ builder.newSection();
+ builder.currentNode()->setObjectName("Base");
+
+ //Generate a circle of vertices for this layer.
+ QGeometryData tempBase;
+
+ tempBase.appendVertex(QVector3D(0,0,0));
+ tempBase.appendTexCoord(QVector2D(0.5,0.5));
+ for (int slice=0; slice<=slices+1; slice++)
+ {
+ tempBase.appendVertex(QVector3D(radius * sliceCos[slice], radius * sliceSin[slice], 0));
+ tempBase.appendTexCoord(QVector2D(0.5*sliceCos[slice]+0.5, 0.5*sliceSin[slice]+0.5));
+ }
+
+ //we need to reverse the above to draw it properly - windings!
+ builder.addTriangulatedFace(tempBase.reversed());
+ }
+ return builder;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/threed/geometry/qgldome.h b/src/threed/geometry/qgldome.h
new file mode 100644
index 000000000..23d86f508
--- /dev/null
+++ b/src/threed/geometry/qgldome.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLDOME_H
+#define QGLDOME_H
+
+#include "qt3dglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLBuilder;
+
+class Q_QT3D_EXPORT QGLDome
+{
+public:
+ explicit QGLDome(qreal diameter = 1.0f, int depth = 3, bool baseEnabled = true)
+ : m_diameter(diameter), m_subdivisionDepth(depth), m_baseEnabled(baseEnabled) {}
+ virtual ~QGLDome();
+
+ qreal diameter() const { return m_diameter; }
+ void setDiameter(qreal diameter) { m_diameter = diameter; }
+
+ int subdivisionDepth() const { return m_subdivisionDepth; }
+ void setSubdivisionDepth(int depth) { m_subdivisionDepth = depth; }
+
+ bool baseEnabled() const {return m_baseEnabled; }
+ void setBaseEnabled(bool baseEnabled) {m_baseEnabled = baseEnabled;}
+
+private:
+ qreal m_diameter;
+ int m_subdivisionDepth;
+ bool m_baseEnabled;
+};
+
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLDome& dome);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/geometry/qglmaterialcollection.cpp b/src/threed/geometry/qglmaterialcollection.cpp
new file mode 100644
index 000000000..674dac70f
--- /dev/null
+++ b/src/threed/geometry/qglmaterialcollection.cpp
@@ -0,0 +1,415 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglmaterialcollection.h"
+#include "qglmaterial_p.h"
+#include <QtCore/qlist.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLMaterialCollection
+ \brief The QGLMaterialCollection class manages groups of materials.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::enablers
+
+ Managing more complex 3d graphics with several materials is easier when the
+ materials can be referred to as a collection. This is the role of the
+ QGLMaterialCollection class.
+
+ Plug-ins implementing 3D formats may make the materials defined in
+ the format available to the application via a QGLMaterialCollection.
+
+ The collection is also optimised for the case where many small objects
+ must refer to materials - such as faces in a mesh, or particles. In
+ this case the materials can be specified as a short data type using an
+ offset into the collection, rather than the material name.
+
+ When building up a collection, meshes that refer to the various materials
+ can check off which ones are used by calling markMaterialAsUsed(), and then
+ remove spurious unused materials by calling removeUnusedMaterials(). This
+ technique is suitable for models loaded from a model file where a large
+ number of materials may be specified but only a few of those materials
+ are used by the particular mesh selected from the scene.
+
+ To make a material available from a collection, call addMaterial(). To
+ retrieve a material from the collection call removeMaterial().
+
+ The collection takes ownership of the QGLMaterial
+ objects passed to it by the addMaterial() function. These
+ objects will be destroyed when the collection is destroyed.
+*/
+
+class QGLMaterialCollectionPrivate
+{
+public:
+ QGLMaterialCollectionPrivate()
+ {
+ }
+
+ QList<QGLMaterial *> materials;
+ QHash<QString, int> materialNames;
+};
+
+/*!
+ Construct a new empty QGLMaterialCollection object. The \a parent
+ is set as the parent of this object.
+*/
+QGLMaterialCollection::QGLMaterialCollection(QObject *parent)
+ : QObject(parent)
+ , d_ptr(new QGLMaterialCollectionPrivate)
+{
+}
+
+/*!
+ Destroy this collection. All material objects referred to by this
+ collection will be destroyed.
+*/
+QGLMaterialCollection::~QGLMaterialCollection()
+{
+ // The QGLMaterial QObject's are reparented to the collection
+ // when addMaterial() is called, so the QObject destructor
+ // will take care of cleaning them up for us.
+}
+
+/*!
+ Returns a pointer to the material corresponding to \a index; or null
+ if \a index is out of range or the material has been removed.
+
+ Here's an example of searching for a material with a given ambient
+ \c{color} in the collection \c{materials}:
+
+ \code
+ for (int colorIndex; colorIndex < materials->size(); ++colorIndex) {
+ if (material(colorIndex) &&
+ material(colorIndex)->ambientColor() == color)
+ break;
+ }
+ if (colorIndex < materials->size())
+ myObject->setMaterial(colorIndex);
+ \endcode
+*/
+QGLMaterial *QGLMaterialCollection::material(int index) const
+{
+ Q_D(const QGLMaterialCollection);
+ return d->materials.value(index, 0);
+}
+
+/*!
+ \overload
+
+ Returns the material associated with \a name in this collection;
+ null if \a name is not present or the material has been removed.
+*/
+QGLMaterial *QGLMaterialCollection::material(const QString &name) const
+{
+ Q_D(const QGLMaterialCollection);
+ int index = d->materialNames.value(name, -1);
+ if (index >= 0)
+ return d->materials[index];
+ else
+ return 0;
+}
+
+/*!
+ Returns true if this collection contains \a material; false otherwise.
+
+ \sa indexOf()
+*/
+bool QGLMaterialCollection::contains(QGLMaterial *material) const
+{
+ return material && material->d_func()->collection == this;
+}
+
+/*!
+ \overload
+
+ Returns true if this collection contains a material called \a name;
+ false otherwise.
+
+ \sa indexOf()
+*/
+bool QGLMaterialCollection::contains(const QString &name) const
+{
+ Q_D(const QGLMaterialCollection);
+ return d->materialNames.contains(name);
+}
+
+/*!
+ Returns the index of \a material in this collection; -1 if
+ \a material is not present in this collection.
+
+ \sa contains()
+*/
+int QGLMaterialCollection::indexOf(QGLMaterial *material) const
+{
+ if (material && material->d_func()->collection == this)
+ return material->d_func()->index;
+ else
+ return -1;
+}
+
+/*!
+ \overload
+
+ Returns the index of the material called \a name in this collection;
+ -1 if \a name is not present in this collection.
+
+ \sa contains()
+*/
+int QGLMaterialCollection::indexOf(const QString &name) const
+{
+ Q_D(const QGLMaterialCollection);
+ return d->materialNames.value(name, -1);
+}
+
+/*!
+ Returns the name of the material at \a index in this material collection;
+ a null QString if \a index is out of range.
+*/
+QString QGLMaterialCollection::materialName(int index) const
+{
+ Q_D(const QGLMaterialCollection);
+ if (index >= 0 && index < d->materials.count()) {
+ QGLMaterial *material = d->materials[index];
+ if (material) {
+ // Use the name in the private data block just in case the
+ // application has modified objectName() since adding.
+ return material->d_func()->name;
+ }
+ }
+ return QString();
+}
+
+/*!
+ Returns true if the material at \a index in this collection has been
+ marked as used by markMaterialAsUsed().
+
+ \sa markMaterialAsUsed()
+*/
+bool QGLMaterialCollection::isMaterialUsed(int index) const
+{
+ QGLMaterial *mat = material(index);
+ if (mat)
+ return mat->d_func()->used;
+ else
+ return false;
+}
+
+/*!
+ Flags the material corresponding to the \a index as used. Some model files
+ may contain a range of materials, applying to various objects in the scene.
+
+ When a particular object is loaded from the file, many of those
+ materials may not be used in that object. This wastes space,
+ with many spurious materials being stored.
+
+ Use this method during model loading or construction to mark off
+ materials that have been used. Materials so marked will not
+ be removed by removeUnusedMaterials().
+
+ \sa removeUnusedMaterials(), isMaterialUsed()
+*/
+void QGLMaterialCollection::markMaterialAsUsed(int index)
+{
+ QGLMaterial *mat = material(index);
+ if (mat)
+ mat->d_func()->used = true;
+}
+
+/*!
+ Removes and deletes materials which have not been marked as used.
+
+ \sa markMaterialAsUsed(), isMaterialUsed()
+*/
+void QGLMaterialCollection::removeUnusedMaterials()
+{
+ Q_D(QGLMaterialCollection);
+ for (int index = 0; index < d->materials.size(); ++index) {
+ QGLMaterial *material = d->materials[index];
+ if (material && !material->d_func()->used)
+ delete removeMaterial(index);
+ }
+}
+
+/*!
+ Adds \a material to this collection and returns its new index. The
+ collection takes ownership of the material and will delete it when the
+ collection is destroyed. Initially the \a material is marked as unused.
+
+ The QObject::objectName() of \a material at the time addMaterial()
+ is called will be used as the material's name within this collection.
+ Changes to the object name after the material is added are ignored.
+
+ If \a material is already present in this collection, then this
+ function will return the index that was previously assigned.
+
+ Returns -1 if \a material has been added to another collection.
+
+ \sa removeMaterial(), markMaterialAsUsed()
+*/
+int QGLMaterialCollection::addMaterial(QGLMaterial *material)
+{
+ Q_D(QGLMaterialCollection);
+ Q_ASSERT(material);
+
+ // Allocate a new index for the material.
+ int index = d->materials.count();
+
+ // Record the index in the private data attached to the material.
+ // This allows us to find the material's index quickly later.
+ QGLMaterialPrivate *dm = material->d_func();
+ if (dm->collection) {
+ if (dm->collection == this)
+ return dm->index;
+ return -1;
+ }
+ dm->collection = this;
+ dm->index = index;
+ dm->name = material->objectName();
+ dm->used = false;
+
+ // Add the material to this collection.
+ material->setParent(this);
+ d->materials.append(material);
+ if (!dm->name.isEmpty())
+ d->materialNames[dm->name] = index;
+ connect(material, SIGNAL(destroyed()), this, SLOT(materialDeleted()));
+ return index;
+}
+
+/*!
+ Removes all instances of \a material from this collection.
+ The \a material object is not deleted and can be reused.
+
+ Does nothing if \a material is null or not a member of
+ this collection.
+
+ \sa addMaterial()
+*/
+void QGLMaterialCollection::removeMaterial(QGLMaterial *material)
+{
+ Q_D(QGLMaterialCollection);
+ if (!material)
+ return;
+
+ // Check the material's owning collection.
+ QGLMaterialPrivate *dm = material->d_func();
+ if (dm->collection != this)
+ return;
+
+ // Remove the material from the collection.
+ d->materials[dm->index] = 0;
+ if (!dm->name.isEmpty())
+ d->materialNames.remove(dm->name);
+ material->setParent(0);
+
+ // Detach from the owning collection.
+ dm->collection = 0;
+ dm->index = -1;
+}
+
+/*!
+ Removes the material at \a index from this collection, and returns
+ a pointer to the material.
+
+ Since the collection is designed for fast lookup by index, the
+ the stored material pointer is set to null but the \a index
+ otherwise remains valid.
+*/
+QGLMaterial *QGLMaterialCollection::removeMaterial(int index)
+{
+ Q_D(QGLMaterialCollection);
+
+ // Bail out if the material is invalid.
+ if (index < 0 || index >= d->materials.count())
+ return 0;
+ QGLMaterial *material = d->materials[index];
+ if (!material)
+ return 0;
+
+ // Remove the material from the collection.
+ QGLMaterialPrivate *dm = material->d_func();
+ d->materials[index] = 0;
+ if (!dm->name.isEmpty())
+ d->materialNames.remove(dm->name);
+ material->setParent(0);
+
+ // Detach from the owning collection.
+ dm->collection = 0;
+ dm->index = -1;
+ return material;
+}
+
+/*!
+ Returns true if this collection is empty, false otherwise.
+
+ \sa size()
+*/
+bool QGLMaterialCollection::isEmpty() const
+{
+ Q_D(const QGLMaterialCollection);
+ return d->materials.isEmpty();
+}
+
+/*!
+ Returns the number of (possibly null) materials in this collection.
+ Null materials result from calling removeMaterial().
+
+ \sa isEmpty()
+*/
+int QGLMaterialCollection::size() const
+{
+ Q_D(const QGLMaterialCollection);
+ return d->materials.size();
+}
+
+/*!
+ \internal
+ Responds to the destroyed() signal by calling removeMaterial() on the
+ material about to be deleted;
+*/
+void QGLMaterialCollection::materialDeleted()
+{
+ removeMaterial(qobject_cast<QGLMaterial *>(sender()));
+}
diff --git a/src/threed/geometry/qglmaterialcollection.h b/src/threed/geometry/qglmaterialcollection.h
new file mode 100644
index 000000000..52ec79bec
--- /dev/null
+++ b/src/threed/geometry/qglmaterialcollection.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLMATERIALCOLLECTION_H
+#define QGLMATERIALCOLLECTION_H
+
+#include <QtCore/qobject.h>
+
+#include "qt3dglobal.h"
+#include "qglmaterial.h"
+#include "qgltexture2d.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLMaterialCollectionPrivate;
+
+class Q_QT3D_EXPORT QGLMaterialCollection : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QGLMaterialCollection)
+ Q_DISABLE_COPY(QGLMaterialCollection)
+public:
+ QGLMaterialCollection(QObject *parent = 0);
+ virtual ~QGLMaterialCollection();
+
+ QGLMaterial *material(int index) const;
+ QGLMaterial *material(const QString &name) const;
+
+ bool contains(QGLMaterial *material) const;
+ bool contains(const QString &name) const;
+
+ int indexOf(QGLMaterial *material) const;
+ int indexOf(const QString &name) const;
+
+ QString materialName(int index) const;
+
+ bool isMaterialUsed(int index) const;
+ void markMaterialAsUsed(int index);
+ void removeUnusedMaterials();
+
+ int addMaterial(QGLMaterial *material);
+ void removeMaterial(QGLMaterial *material);
+ QGLMaterial *removeMaterial(int index);
+
+ bool isEmpty() const;
+ int size() const;
+
+private Q_SLOTS:
+ void materialDeleted();
+
+private:
+ QScopedPointer<QGLMaterialCollectionPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLMATERIALCOLLECTION_H
diff --git a/src/threed/geometry/qglsection.cpp b/src/threed/geometry/qglsection.cpp
new file mode 100644
index 000000000..3775af4ee
--- /dev/null
+++ b/src/threed/geometry/qglsection.cpp
@@ -0,0 +1,696 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglsection_p.h"
+#include "qglbuilder_p.h"
+#include "qarray.h"
+#include "qvector_utils_p.h"
+
+#include <QtGui/qvector3d.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qbitarray.h>
+
+#include <limits.h>
+
+/*!
+ \internal
+ \class QGLSection
+ \brief The QGLSection class clusters like geometry in a QGLBuilder.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ QGLSection instances partition a QGLBuilder into related sections,
+ while the builder is being initialized with geometry data.
+
+ Once the builder is initialized, and geometry building is complete
+ the QGLSection instances are destroyed and the data is uploaded to the
+ graphics hardware.
+
+ The QGLSection class is a work horse for the QGLBuilder, and it
+ takes care of automatically managing vertex data. As such
+ for usual use cases, its functionality will not need to be referenced
+ directly. For low-level access to geometry, QGLSection provides a
+ range of accessors to reference geometry data during scene building.
+
+ Within a section, incoming geometry data will be coalesced and
+ indexes created to reference the fewest possible copies of the vertex
+ data. For example, in smooth geometry all copies of a vertex are
+ coalesced into one, and referenced by indices - except in the case
+ where texture data forms a \i seam and a copy must be created to carry
+ the two texture coordinates of the seam.
+
+ This is handled automatically by QGLSection, to pack data into the
+ smallest space possible thus improving cache coherence and performance.
+
+ All the vertices in a QGLSection are treated with the same
+ \l{QGL::Smoothing}{smoothing}, and have the same
+ \l{QLogicalVertex::Type}{data types}.
+
+ Each QGLSection references a contiguous range of vertices in a
+ QGLBuilder.
+
+ A QGLBuilder instance has the \l{QGLBuilder::newSection()}{newSection()}
+ function which creates a new QGLSection to reference its data. Use this
+ to construct new QGLSection instances, or alternatively construct
+ a new QGLSection() and pass a non-null QGLBuilder pointer.
+
+ These functions all return QVector values. QVector instances are
+ implicitly shared, thus the copies are inexpensive unless a
+ non-const function is called on them, triggering a copy-on-write.
+
+ Generally for adding geometry, use append(). This function simply
+ calls virtual protected functions appendSmooth() (for smoothed vertices)
+ and appendFaceted() (for faceted vertices). See QGLBuilder for a
+ discussion of smoothing.
+*/
+
+// allow QVector3D's to be stored in a QMap
+inline bool operator<(const QVector3D &a, const QVector3D &b)
+{
+ if (qFskCompare(a.x(), b.x()))
+ {
+ if (qFskCompare(a.y(), b.y()))
+ {
+ if (qFskCompare(a.z(), b.z()))
+ {
+ return false; // equal so not less-than
+ }
+ else
+ {
+ return a.z() < b.z();
+ }
+ }
+ else
+ {
+ return a.y() < b.y();
+ }
+ }
+ else
+ {
+ return a.x() < b.x();
+ }
+}
+
+static inline bool qSameDirection(const QVector3D &a , const QVector3D &b)
+{
+ bool res = false;
+ if (!a.isNull() && !b.isNull())
+ {
+ float dot = QVector3D::dotProduct(a, b);
+ res = qFskCompare((qreal)dot, a.length() * b.length());
+ }
+ return res;
+}
+
+class QGLSectionPrivate
+{
+public:
+ QGLSectionPrivate(const QVector3DArray *ary)
+ : vec_data(ary)
+ , it(vec_map.end())
+ , map_threshold(5)
+ , number_mapped(0)
+ , start_ptr(-1)
+ , end_ptr(-1)
+ {
+ normIndices.fill(-1, 32);
+ }
+
+ ~QGLSectionPrivate() {}
+
+ bool normalAccumulated(int index, const QVector3D &norm) const
+ {
+ if (index >= normIndices.size())
+ return false;
+ int ptr = normIndices.at(index);
+ while (ptr != -1)
+ {
+ int val_ptr = normPtrs.at(ptr);
+ //if (normValues.at(val_ptr) == norm)
+ if (qSameDirection(normValues.at(val_ptr), norm))
+ return true;
+ ptr = normPtrs.at(ptr+1);
+ }
+ return false;
+ }
+
+ void accumulateNormal(int index, const QVector3D &norm)
+ {
+ int new_norm_index = normValues.size();
+ normValues.append(norm);
+ if (normIndices.size() <= index)
+ {
+ int old_size = normIndices.size();
+ normIndices.extend(32);
+ for (int i = old_size; i < normIndices.size(); ++i)
+ normIndices[i] = -1;
+ }
+ int new_norm_ptr = normPtrs.size();
+ normPtrs.append(new_norm_index); // even ptrs point to vector value
+ normPtrs.append(-1); // odd ptrs point to next in normPtr linked list
+ if (normIndices.at(index) == -1)
+ {
+ normIndices[index] = new_norm_ptr;
+ }
+ else
+ {
+ int norm_ptr = normIndices.at(index);
+ while (normPtrs.at(norm_ptr + 1) != -1)
+ {
+ norm_ptr = normPtrs.at(norm_ptr + 1);
+ }
+ normPtrs[norm_ptr+1] = new_norm_ptr;
+ }
+ }
+
+ void mapVertex(const QVector3D &v, int ix)
+ {
+ Q_UNUSED(ix);
+ Q_UNUSED(v);
+ static bool seeded = false;
+ if (!seeded)
+ qsrand(31415);
+ Q_ASSERT(vec_data->at(ix) == v);
+ if ((vec_data->size() - number_mapped) > map_threshold)
+ {
+ int to_map = vec_data->size() - number_mapped;
+ QArray<int, 100> shuffle(to_map, -1);
+ for (int i = number_mapped, k = 0; i < vec_data->size(); ++i, ++k)
+ shuffle[k] = i;
+ for (int n = to_map; n > 1; --n)
+ {
+ int k = qrand() % n;
+ int tmp = shuffle[k];
+ shuffle[k] = shuffle[n - 1];
+ shuffle[n - 1] = tmp;
+ }
+ for (int i = 0; i < to_map; ++i)
+ vec_map.insertMulti(vec_data->at(shuffle.at(i)), shuffle.at(i));
+ number_mapped += to_map;
+ }
+ }
+
+ int nextIndex()
+ {
+ int result = -1;
+ if (end_ptr != -1)
+ {
+ // first look through the unmapped items
+ while (start_ptr <= end_ptr && result == -1)
+ {
+ // search from the end and beginning, favouring the end - most often
+ // its in the last few we added, sometimes in the first ones
+ if (qFskCompare(vec_data->at(end_ptr--), target))
+ result = end_ptr+1;
+ else if (start_ptr <= end_ptr && qFskCompare(vec_data->at(end_ptr--), target))
+ result = end_ptr+1;
+ else if (start_ptr <= end_ptr && qFskCompare(vec_data->at(start_ptr++), target))
+ result = start_ptr-1;
+ }
+ // if that found nothing, have a look at the map
+ if (result == -1)
+ {
+ start_ptr = -1;
+ end_ptr = -1;
+ it = vec_map.constEnd();
+ if (vec_map.size() > 0)
+ it = vec_map.find(target);
+ }
+ }
+ if (it != vec_map.constEnd())
+ {
+ // if there was something in the map see if its still on target
+ if (qFskCompare(it.key(), target))
+ {
+ result = it.value();
+ ++it; // increment to find more insertMulti instances
+ }
+ else
+ {
+ // not on target - flag that we're done here
+ it = vec_map.constEnd();
+ }
+ }
+ return result;
+ }
+
+ int findVertex(const QVector3D &v)
+ {
+ end_ptr = vec_data->size() - 1; // last one not in QMap
+ start_ptr = number_mapped; // first one not in QMap
+ target = v;
+ return nextIndex();
+ }
+
+ // mapper
+ int index;
+ QVector3D target;
+ const QVector3DArray *vec_data;
+ QMap<QVector3D, int> vec_map;
+ QMap<int, int> index_map;
+ QMap<QVector3D,int>::const_iterator it;
+ int map_threshold; // if more than this is unmapped, do a mapping run
+ int number_mapped; // how many vertices have been mapped
+ int start_ptr;
+ int end_ptr;
+
+ QArray<int, 32> normIndices;
+ QArray<int, 32> normPtrs;
+ QArray<QVector3D, 32> normValues;
+
+ QList<QGLSceneNode*> nodes;
+};
+
+/*!
+ \internal
+ Construct a new QGLSection on \a builder, and with smoothing \a s.
+ By default the smoothing is QGL::Smooth.
+
+ See QGLBuilder for a discussion of smoothing.
+
+ The pointer \a list must be non-null, and in debug mode, unless QT_NO_DEBUG is
+ defined, this function will assert if \a list is null.
+
+ The following lines of code have identical effect:
+ \code
+ QGLSection *s = myDisplayList->newSection(QGL::Faceted);
+ QGLSection *s2 = new QGLSection(myDisplayList, QGL::Faceted);
+ \endcode
+*/
+QGLSection::QGLSection(QGLBuilder *builder, QGL::Smoothing s)
+ : m_smoothing(s)
+ , d(0)
+{
+ Q_ASSERT(builder);
+ enableField(QGL::Position);
+ Q_ASSERT(vertexData());
+ d = new QGLSectionPrivate(vertexData());
+ builder->addSection(this);
+}
+
+/*!
+ \internal
+ Destroy this QGLSection, recovering any resources.
+*/
+QGLSection::~QGLSection()
+{
+ delete d;
+}
+
+/*!
+ \internal
+ Reserve capacity for \a amount items. This may avoid realloc
+ overhead when a large number of items will be appended.
+*/
+void QGLSection::reserve(int amount)
+{
+ QGeometryData::reserve(amount);
+ d->normIndices.reserve(amount);
+ d->normPtrs.reserve(amount * 2);
+ d->normValues.reserve(amount);
+}
+
+/*!
+ \internal
+ Adds the logical vertices \a a, \a b and \c to this section. All
+ should have the same fields. This function is exactly equivalent to
+ \code
+ append(a); append(b); append(c);
+ \endcode
+
+ \sa appendSmooth(), appendFaceted()
+*/
+void QGLSection::append(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c)
+{
+ Q_ASSERT(a.fields() == b.fields() && b.fields() == c.fields());
+ if (!a.hasField(QGL::Normal))
+ {
+ appendFaceted(a, b, c);
+ }
+ else
+ {
+ if (m_smoothing == QGL::Smooth)
+ appendSmooth(a, b, c);
+ else
+ appendFaceted(a, b, c);
+ }
+}
+
+/*!
+ \internal
+ Adds the logical vertex \a lv to this section.
+
+ Otherwise, if the \a lv does have a lighting normal; then the
+ vertex processing depends on the smoothing property of this section.
+ If this section has smoothing QGL::Smooth, then the append will be done
+ by calling appendSmooth(); or if this section has smoothing QGL::Faceted,
+ then the append will be done by calling appendFaceted().
+
+ \sa appendSmooth(), appendFaceted()
+*/
+void QGLSection::append(const QLogicalVertex &lv)
+{
+ if (!lv.hasField(QGL::Normal))
+ {
+ appendFaceted(lv);
+ }
+ else
+ {
+ if (m_smoothing == QGL::Smooth)
+ appendSmooth(lv);
+ else
+ appendFaceted(lv);
+ }
+}
+
+static bool qCompareByAttributes(const QLogicalVertex &a, const QLogicalVertex &b)
+{
+ static const quint32 ATTRS_AND_TEXTURES = (0xFFFFFFFF << QGL::TextureCoord0);
+ quint32 af = a.fields() & ATTRS_AND_TEXTURES;
+ quint32 bf = b.fields() & ATTRS_AND_TEXTURES;
+ if (af != bf)
+ return false;
+ quint32 flds = af | bf;
+ const quint32 mask = 0x01;
+ flds >>= QGL::TextureCoord0;
+ for (int i = QGL::TextureCoord0; flds; ++i, flds >>= 1)
+ {
+ if (flds & mask)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(i);
+ if (attr < QGL::CustomVertex0)
+ {
+ if (!qFskCompare(a.texCoord(attr), b.texCoord(attr)))
+ return false;
+ }
+ else
+ {
+ QVariant v1 = a.attribute(attr);
+ QVariant v2 = b.attribute(attr);
+ if (v1.type() == (QVariant::Type)QMetaType::Float)
+ return qFskCompare(v1.toFloat(), v2.toFloat());
+ else if (v1.type() == QVariant::Vector2D)
+ return qFskCompare(qVariantValue<QVector2D>(v1), qVariantValue<QVector2D>(v2));
+ else if (v1.type() == QVariant::Vector3D)
+ return qFskCompare(qVariantValue<QVector3D>(v1), qVariantValue<QVector3D>(v2));
+ else
+ return v1 == v2;
+ }
+ }
+ }
+ return true;
+}
+
+int QGLSection::appendOne(const QLogicalVertex &lv)
+{
+#ifndef QT_NO_DEBUG_STREAM
+ if (count() && lv.fields() != fields())
+ {
+ qDebug() << "Warning: adding" << lv << "fields:" << lv.fields()
+ << "fields do not match existing:" << fields()
+ << "create new section first?";
+ }
+#endif
+ int index = appendVertex(lv);
+ d->mapVertex(lv.vertex(), index);
+ appendIndex(index);
+ return index;
+}
+
+/*!
+ \internal
+ Adds the logical vertex \a lv to this section of a builder.
+
+ Two QLogicalVertex instances a and b are treated as being duplicates for
+ the purpose of smoothing, if \c{qFuzzyCompare(a.vertex(), b.vertex())} is
+ true
+
+ All duplicate occurrences of a vertex are coalesced, that is replaced
+ by a GL index referencing the one copy.
+
+ In order to draw \a lv as part of a smooth continuous surface, with
+ no distinct edge, duplicates of vertex \a lv are coalesced into one
+ (within this section) and the normal for that one set to the average of
+ the incoming unique normals.
+
+ The incoming vertex \a lv is not treated as a duplicate if \a lv has
+ different texture coordinates or attributes. This occurs for example
+ in the case of a texture seam, where two different texture coordinates
+ are required at the same point on the geometry.
+
+ In that case a new duplicate vertex is added to carry the unique
+ texture coordinates or attributes. When new vertex copies are added in
+ this way all copies receive the averaged normals.
+
+ Call this function to add the vertices of a smooth face to the section
+ of a builder, or use:
+
+ \code
+ myDisplayList->newSection(QGLBuilder::Smooth);
+ myDisplayList->addTriangle(a, b, c);
+ \endcode
+
+ In smooth surfaces, the vertex and its normal is only sent to the
+ graphics hardware once (not once per face), thus smooth geometry may
+ consume fewer resources.
+
+ \sa appendFaceted(), updateTexCoord(), QGLBuilder::newSection()
+*/
+void QGLSection::appendSmooth(const QLogicalVertex &lv)
+{
+ Q_ASSERT(lv.hasField(QGL::Position));
+ Q_ASSERT(lv.hasField(QGL::Normal));
+
+ int found_index = d->findVertex(lv.vertex());
+ bool coalesce = false;
+ if (found_index == -1)
+ {
+ int newIndex = appendOne(lv);
+ d->accumulateNormal(newIndex, lv.normal());
+ }
+ else
+ {
+ while (!coalesce && found_index != -1)
+ {
+ if (qCompareByAttributes(lv, logicalVertexAt(found_index)))
+ coalesce = true;
+ else
+ found_index = d->nextIndex();
+ }
+ if (!coalesce) // texture or attributes prevented coalesce
+ {
+ // new vert to carry tex/attrib data
+ d->accumulateNormal(appendOne(lv), lv.normal());
+ }
+ else
+ {
+ appendIndex(found_index);
+ while (found_index != -1)
+ {
+ if (!d->normalAccumulated(found_index, lv.normal()))
+ {
+ normal(found_index) += lv.normal();
+ d->accumulateNormal(found_index, lv.normal());
+ }
+ found_index = d->nextIndex();
+ }
+ }
+ }
+}
+
+
+void QGLSection::appendSmooth(const QLogicalVertex &lv, int index)
+{
+ Q_ASSERT(lv.hasField(QGL::Position));
+ Q_ASSERT(lv.hasField(QGL::Normal));
+
+ int found_index = -1;
+ QMap<int, int>::const_iterator it = d->index_map.constFind(index);
+ if (it != d->index_map.constEnd())
+ found_index = it.value();
+ if (found_index == -1)
+ {
+ int newIndex = appendVertex(lv);
+ d->index_map.insert(index, newIndex);
+ appendIndex(newIndex);
+ d->accumulateNormal(newIndex, lv.normal());
+ }
+ else
+ {
+ appendIndex(found_index);
+ if (!d->normalAccumulated(found_index, lv.normal()))
+ {
+ normal(found_index) += lv.normal();
+ d->accumulateNormal(found_index, lv.normal());
+ }
+ }
+}
+
+/*!
+ \internal
+ Add the logical vertex \a lv to this section of a builder.
+
+ The vertex will be drawn as a distinct edge, instead of just part of a
+ continuous smooth surface. To acheive this the vertex value of \a lv
+ is duplicated for each unique normal in the current section.
+
+ Note that duplication is only for unique normals: if a vertex is already
+ present with a given normal it is coalesced and simply referenced by index.
+ As for appendSmooth() vertices are not coalesced in this way if \a lv
+ has a different texture coordinate or attribute than its duplicate.
+
+ In faceted surfaces, the vertex is sent to the graphics hardware once for
+ each normal it has, and thus may consume more resources.
+
+ \sa appendSmooth(), updateTexCoord(), QGLBuilder::newSection()
+*/
+void QGLSection::appendFaceted(const QLogicalVertex &lv)
+{
+ Q_ASSERT(lv.hasField(QGL::Position));
+ int found_index = d->findVertex(lv.vertex());
+ bool coalesce = false;
+ while (!coalesce && found_index != -1)
+ {
+ if (logicalVertexAt(found_index) == lv)
+ coalesce = true;
+ else
+ found_index = d->nextIndex();
+ }
+ if (coalesce) // found
+ {
+ appendIndex(found_index);
+ }
+ else
+ {
+ appendOne(lv);
+ }
+}
+
+/*!
+ \internal
+ Returns the current map threshold for this section. The threshold is the
+ point at which the section switches from using a plain QArray - with
+ linear performance ie O(n) - to using a QMap - with approx O(log n). These
+ structures are used for looking up vertices during the index generation and
+ normals calculation.
+
+ The default value is 50.
+
+ \sa setMapThreshold()
+*/
+int QGLSection::mapThreshold() const
+{
+ return d->map_threshold;
+}
+
+/*!
+ \internal
+ Sets the current map threshold to \a t for this section.
+
+ \sa mapThreshold()
+*/
+void QGLSection::setMapThreshold(int t)
+{
+ d->map_threshold = t;
+}
+
+/*!
+ \internal
+ Returns a list of the QGLSceneNode instances associated with this section.
+*/
+QList<QGLSceneNode*> QGLSection::nodes() const
+{
+ return d->nodes;
+}
+
+/*!
+ \internal
+ Adds the \a node to the list of QGLSceneNode instances associated with
+ this section.
+*/
+void QGLSection::addNode(QGLSceneNode *node)
+{
+ d->nodes.append(node);
+}
+
+/*!
+ \internal
+ Deletes the \a node from the list of QGLSceneNode instances associated
+ with this section. Returns true if the \a node was found, false
+ otherwise.
+*/
+bool QGLSection::deleteNode(QGLSceneNode *node)
+{
+ int ix = d->nodes.indexOf(node);
+ if (ix != -1)
+ {
+ d->nodes.removeAt(ix);
+ return true;
+ }
+ return false;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \internal
+ Output a string representation of \a section to a debug stream \a dbg.
+ \relates QGLSection
+*/
+QDebug operator<<(QDebug dbg, const QGLSection &section)
+{
+ dbg.space()
+ << "QGLSection(" << &section
+ << "- count:" << section.count()
+ << "- smoothing mode:" << (section.smoothing() == QGL::Smooth ?
+ "QGL::Smooth" : "QGL::Faceted") << "\n";
+ QGL::IndexArray indices = section.indices();
+ for (int i = 0; i < section.count(); ++i)
+ {
+ int ix = indices[i];
+ dbg << section.logicalVertexAt(ix) << "\n";
+ }
+ dbg << ")\n";
+ return dbg.space();
+}
+#endif
diff --git a/src/threed/geometry/qglsection_p.h b/src/threed/geometry/qglsection_p.h
new file mode 100644
index 000000000..ea48ff1b1
--- /dev/null
+++ b/src/threed/geometry/qglsection_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLSECTION_H
+#define QGLSECTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qglpainter.h"
+#include "qlogicalvertex.h"
+#include "qbox3d.h"
+#include "qglnamespace.h"
+
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qmatrix4x4.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGLPainter;
+class QGLBuilder;
+class QGLSectionPrivate;
+class QGeometryData;
+class QGLSceneNode;
+
+class Q_QT3D_EXPORT QGLSection : public QGeometryData
+{
+public:
+ QGLSection(QGLBuilder *d, QGL::Smoothing sm = QGL::Smooth);
+ ~QGLSection();
+
+ void reserve(int amount);
+
+ void append(const QLogicalVertex &lv);
+ void append(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c);
+ void appendSmooth(const QLogicalVertex &lv);
+ void appendSmooth(const QLogicalVertex &lv, int index);
+ void appendSmooth(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c)
+ {
+ appendSmooth(a);
+ appendSmooth(b);
+ appendSmooth(c);
+ }
+ void appendFaceted(const QLogicalVertex &lv);
+ void appendFaceted(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c)
+ {
+ appendFaceted(a);
+ appendFaceted(b);
+ appendFaceted(c);
+ }
+
+ inline QGL::Smoothing smoothing() const;
+ inline void setSmoothing(QGL::Smoothing s);
+ int mapThreshold() const;
+ void setMapThreshold(int);
+ QList<QGLSceneNode*> nodes() const;
+ void addNode(QGLSceneNode *node);
+ bool deleteNode(QGLSceneNode *node);
+private:
+ Q_DISABLE_COPY(QGLSection);
+ friend class QGLBuilder;
+
+ int appendOne(const QLogicalVertex &vertex);
+
+ QGL::Smoothing m_smoothing;
+ QGLSectionPrivate *d;
+};
+
+inline QGL::Smoothing QGLSection::smoothing() const
+{
+ return m_smoothing;
+}
+
+inline void QGLSection::setSmoothing(QGL::Smoothing s)
+{
+ m_smoothing = s;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QGLSection &section);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QGLSECTION_H
diff --git a/src/threed/geometry/qglsphere.cpp b/src/threed/geometry/qglsphere.cpp
new file mode 100644
index 000000000..9a8976ac3
--- /dev/null
+++ b/src/threed/geometry/qglsphere.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglsphere.h"
+#include "qglbuilder.h"
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLSphere
+ \brief The QGLSphere class represents the geometry of a simple sphere in 3D space.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The following example creates a sphere of 2 units in diameter and
+ draws it at (10, 25, 0) in a QGLPainter:
+
+ \code
+ QGLBuilder builder;
+ builder << QGLSphere(2);
+ QGLSceneNode *node = builder.finalizedSceneNode();
+
+ painter.translate(10, 25, 0);
+ node->draw(&painter);
+ \endcode
+
+ The QGLSphere class specifies positions, normals and 2D texture
+ co-ordinates for all of the vertices that make up the sphere.
+
+ The texture co-ordinates are fixed at construction time. This
+ is because constructing the sphere can involve generating additional
+ vertices which need to interpolate the texture co-ordinates of their
+ neighboring vertices.
+
+ The default mode of QGLSphere is a "UV sphere", which divides the
+ sphere up into longitudinal and latitudinal sections. The longitudinal
+ slices meet at the poles, which in a single unit sphere are defined to
+ be at (0, 0, +0.5) and (0, 0, -0.5). This choice is the simplest to
+ texture map as the texture will only distort along the x-axis of the
+ 2D texture. However the density of vertices is significantly higher at
+ the poles than it is elsewhere on the sphere and is a poor choice if a
+ uniform density of pixels from the texture map is required.
+
+ \sa QGLBuilder
+*/
+
+/*!
+ \fn QGLSphere::QGLSphere(qreal diameter, int depth)
+
+ Creates a sphere of \a diameter across (default is 1). When the sphere
+ is recursively subdivided into triangles, it will be subdivided no more
+ than \a depth times (between 1 and 10, default is 5).
+*/
+
+/*!
+ Destroys this sphere object.
+*/
+QGLSphere::~QGLSphere()
+{
+}
+
+/*!
+ \fn qreal QGLSphere::diameter() const
+
+ Returns the diameter of this sphere. The default is 1.
+
+ \sa setDiameter()
+*/
+
+/*!
+ \fn void QGLSphere::setDiameter(qreal diameter)
+
+ Sets the diameter of this sphere to \a diameter.
+
+ \sa diameter()
+*/
+
+/*!
+ \fn int QGLSphere::subdivisionDepth() const
+
+ Returns the maximum depth when this sphere is subdivided into triangles.
+ The default is 5. The following picture shows the effect of depth
+ values between 1 and 10 for a UV sphere:
+
+ \image sphere-detail.png
+
+ \table
+ \header \o Level of Detail \o Number of Triangles
+ \row \o 1 \o 64
+ \row \o 2 \o 128
+ \row \o 3 \o 256
+ \row \o 4 \o 512
+ \row \o 5 \o 1024
+ \row \o 6 \o 2048
+ \row \o 7 \o 4096
+ \row \o 8 \o 8192
+ \row \o 9 \o 16384
+ \row \o 10 \o 32768
+ \endtable
+
+ \sa setSubdivisionDepth()
+*/
+
+/*!
+ \fn void QGLSphere::setSubdivisionDepth(int depth)
+
+ Sets the maximum \a depth when this sphere is subdivided into triangles.
+
+ \sa subdivisionDepth()
+*/
+
+/*!
+ \relates QGLSphere
+
+ Builds the geometry for \a sphere within the specified
+ geometry \a builder.
+*/
+QGLBuilder& operator<<(QGLBuilder& builder, const QGLSphere& sphere)
+{
+ qreal radius = sphere.diameter() / 2.0f;
+
+ // Determine the number of slices and stacks to generate.
+ static int const slicesAndStacks[] = {
+ 8, 4,
+ 8, 8,
+ 16, 8,
+ 16, 16,
+ 32, 16,
+ 32, 32,
+ 64, 32,
+ 64, 64,
+ 128, 64,
+ 128, 128
+ };
+ int divisions = sphere.subdivisionDepth();
+ if (divisions < 1)
+ divisions = 1;
+ else if (divisions > 10)
+ divisions = 10;
+ int stacks = slicesAndStacks[divisions * 2 - 1];
+ int slices = slicesAndStacks[divisions * 2 - 2];
+
+ // Precompute sin/cos values for the slices and stacks.
+ const int maxSlices = 128 + 1;
+ const int maxStacks = 128 + 1;
+ qreal sliceSin[maxSlices];
+ qreal sliceCos[maxSlices];
+ qreal stackSin[maxStacks];
+ qreal stackCos[maxStacks];
+ for (int slice = 0; slice < slices; ++slice) {
+ qreal angle = 2 * M_PI * slice / slices;
+ sliceSin[slice] = qFastSin(angle);
+ sliceCos[slice] = qFastCos(angle);
+ }
+ sliceSin[slices] = sliceSin[0]; // Join first and last slice.
+ sliceCos[slices] = sliceCos[0];
+ for (int stack = 0; stack <= stacks; ++stack) {
+ qreal angle = M_PI * stack / stacks;
+ stackSin[stack] = qFastSin(angle);
+ stackCos[stack] = qFastCos(angle);
+ }
+ stackSin[0] = 0.0f; // Come to a point at the poles.
+ stackSin[stacks] = 0.0f;
+
+ // Create the stacks.
+ for (int stack = 0; stack < stacks; ++stack) {
+ QGeometryData prim;
+ qreal z = radius * stackCos[stack];
+ qreal nextz = radius * stackCos[stack + 1];
+ qreal s = stackSin[stack];
+ qreal nexts = stackSin[stack + 1];
+ qreal c = stackCos[stack];
+ qreal nextc = stackCos[stack + 1];
+ qreal r = radius * s;
+ qreal nextr = radius * nexts;
+ for (int slice = 0; slice <= slices; ++slice) {
+ prim.appendVertex
+ (QVector3D(nextr * sliceSin[slice],
+ nextr * sliceCos[slice], nextz));
+ prim.appendNormal
+ (QVector3D(sliceSin[slice] * nexts,
+ sliceCos[slice] * nexts, nextc));
+ prim.appendTexCoord
+ (QVector2D(1.0f - qreal(slice) / slices,
+ 1.0f - qreal(stack + 1) / stacks));
+
+ prim.appendVertex
+ (QVector3D(r * sliceSin[slice],
+ r * sliceCos[slice], z));
+ prim.appendNormal
+ (QVector3D(sliceSin[slice] * s,
+ sliceCos[slice] * s, c));
+ prim.appendTexCoord
+ (QVector2D(1.0f - qreal(slice) / slices,
+ 1.0f - qreal(stack) / stacks));
+ }
+ builder.addQuadStrip(prim);
+ }
+
+ return builder;
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/geometry/qglsphere.h b/src/threed/geometry/qglsphere.h
new file mode 100644
index 000000000..784f9db4c
--- /dev/null
+++ b/src/threed/geometry/qglsphere.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLSPHERE_H
+#define QGLSPHERE_H
+
+#include "qt3dglobal.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class QGLBuilder;
+
+class Q_QT3D_EXPORT QGLSphere
+{
+public:
+ explicit QGLSphere(qreal diameter = 1.0f, int depth = 5)
+ : m_diameter(diameter), m_subdivisionDepth(depth) {}
+ virtual ~QGLSphere();
+
+ qreal diameter() const { return m_diameter; }
+ void setDiameter(qreal diameter) { m_diameter = diameter; }
+
+ int subdivisionDepth() const { return m_subdivisionDepth; }
+ void setSubdivisionDepth(int depth) { m_subdivisionDepth = depth; }
+
+private:
+ qreal m_diameter;
+ int m_subdivisionDepth;
+};
+
+Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLSphere& sphere);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/geometry/qglteapot.cpp b/src/threed/geometry/qglteapot.cpp
new file mode 100644
index 000000000..fb2419dce
--- /dev/null
+++ b/src/threed/geometry/qglteapot.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qglteapot.h"
+#include "qglteapot_data_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGLTeapot
+ \brief The QGLTeapot class represents a 3D teapot object.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ The classic 3D "Utah Teapot" was originally drawn by Martin Newell
+ in 1975. The vertex data was made publicly available by him and
+ it has been a standard 3D test object ever since.
+
+ For more information on the history of the "Utah Teapot", see
+ Wikipedia, http://en.wikipedia.org/wiki/Utah_teapot.
+
+ The following example code uses QGLTeapot to draw a teapot of size
+ 0.5 at the origin:
+
+ \code
+ QGLBuilder builder;
+ builder << QGLTeapot();
+ teapot = builder.finalizedSceneNode();
+
+ painter.modelViewMatrix().scale(0.5f);
+ teapot->draw(painter);
+ \endcode
+
+ The QGLTeapot object contains a lot of vertex data once it has
+ been subdivided into triangles. It is recommended that instances
+ of this class be created at startup, added to a QGLBuilder,
+ and then the finalized scene node can be reused over and over.
+
+ \sa QGLBezierPatches
+*/
+
+/*!
+ Constructs a new 3D teapot geometry object.
+*/
+QGLTeapot::QGLTeapot()
+{
+ QVector3DArray positions;
+ for (int pindex = 0; pindex < teapotPatchCount * 16; ++pindex) {
+ int vindex = teapotPatchData[pindex];
+ positions.append(teapotBezierVertexData[vindex * 3],
+ teapotBezierVertexData[vindex * 3 + 1],
+ teapotBezierVertexData[vindex * 3 + 2]);
+ }
+ setPositions(positions);
+ setSubdivisionDepth(teapotDepth);
+}
+
+/*!
+ Destroys this teapot geometry object.
+*/
+QGLTeapot::~QGLTeapot()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/threed/geometry/qglteapot.h b/src/threed/geometry/qglteapot.h
new file mode 100644
index 000000000..290ff23d9
--- /dev/null
+++ b/src/threed/geometry/qglteapot.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLTEAPOT_H
+#define QGLTEAPOT_H
+
+#include "qglbezierpatches.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3D)
+
+class Q_QT3D_EXPORT QGLTeapot : public QGLBezierPatches
+{
+public:
+ QGLTeapot();
+ ~QGLTeapot();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/threed/geometry/qglteapot_data_p.h b/src/threed/geometry/qglteapot_data_p.h
new file mode 100644
index 000000000..28dbfa7ac
--- /dev/null
+++ b/src/threed/geometry/qglteapot_data_p.h
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QGLTEAPOT_DATA_P_H
+#define QGLTEAPOT_DATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qglbezierpatches.h"
+
+// Generated from teapot.txt by meshcvt, depth = 4
+
+#define teapotBezierVertexCount 306
+#define teapotBezierVertexStride 3
+#define teapotPatchCount 32
+#define teapotDepth 4
+static float const teapotBezierVertexData[] = {
+ 0.700000f, 0.450000f, -0.000000f,
+ 0.700000f, 0.450000f, 0.392000f,
+ 0.392000f, 0.450000f, 0.700000f,
+ 0.000000f, 0.450000f, 0.700000f,
+ 0.668750f, 0.515625f, -0.000000f,
+ 0.668750f, 0.515625f, 0.374500f,
+ 0.374500f, 0.515625f, 0.668750f,
+ 0.000000f, 0.515625f, 0.668750f,
+ 0.718750f, 0.515625f, -0.000000f,
+ 0.718750f, 0.515625f, 0.402500f,
+ 0.402500f, 0.515625f, 0.718750f,
+ 0.000000f, 0.515625f, 0.718750f,
+ 0.750000f, 0.450000f, -0.000000f,
+ 0.750000f, 0.450000f, 0.420000f,
+ 0.420000f, 0.450000f, 0.750000f,
+ 0.000000f, 0.450000f, 0.750000f,
+ -0.392000f, 0.450000f, 0.700000f,
+ -0.700000f, 0.450000f, 0.392000f,
+ -0.700000f, 0.450000f, -0.000000f,
+ -0.374500f, 0.515625f, 0.668750f,
+ -0.668750f, 0.515625f, 0.374500f,
+ -0.668750f, 0.515625f, -0.000000f,
+ -0.402500f, 0.515625f, 0.718750f,
+ -0.718750f, 0.515625f, 0.402500f,
+ -0.718750f, 0.515625f, -0.000000f,
+ -0.420000f, 0.450000f, 0.750000f,
+ -0.750000f, 0.450000f, 0.420000f,
+ -0.750000f, 0.450000f, -0.000000f,
+ -0.700000f, 0.450000f, -0.392000f,
+ -0.392000f, 0.450000f, -0.700000f,
+ 0.000000f, 0.450000f, -0.700000f,
+ -0.668750f, 0.515625f, -0.374500f,
+ -0.374500f, 0.515625f, -0.668750f,
+ 0.000000f, 0.515625f, -0.668750f,
+ -0.718750f, 0.515625f, -0.402500f,
+ -0.402500f, 0.515625f, -0.718750f,
+ 0.000000f, 0.515625f, -0.718750f,
+ -0.750000f, 0.450000f, -0.420000f,
+ -0.420000f, 0.450000f, -0.750000f,
+ 0.000000f, 0.450000f, -0.750000f,
+ 0.392000f, 0.450000f, -0.700000f,
+ 0.700000f, 0.450000f, -0.392000f,
+ 0.374500f, 0.515625f, -0.668750f,
+ 0.668750f, 0.515625f, -0.374500f,
+ 0.402500f, 0.515625f, -0.718750f,
+ 0.718750f, 0.515625f, -0.402500f,
+ 0.420000f, 0.450000f, -0.750000f,
+ 0.750000f, 0.450000f, -0.420000f,
+ 0.875000f, 0.187500f, -0.000000f,
+ 0.875000f, 0.187500f, 0.490000f,
+ 0.490000f, 0.187500f, 0.875000f,
+ 0.000000f, 0.187500f, 0.875000f,
+ 1.000000f, -0.075000f, -0.000000f,
+ 1.000000f, -0.075000f, 0.560000f,
+ 0.560000f, -0.075000f, 1.000000f,
+ 0.000000f, -0.075000f, 1.000000f,
+ 1.000000f, -0.300000f, -0.000000f,
+ 1.000000f, -0.300000f, 0.560000f,
+ 0.560000f, -0.300000f, 1.000000f,
+ 0.000000f, -0.300000f, 1.000000f,
+ -0.490000f, 0.187500f, 0.875000f,
+ -0.875000f, 0.187500f, 0.490000f,
+ -0.875000f, 0.187500f, -0.000000f,
+ -0.560000f, -0.075000f, 1.000000f,
+ -1.000000f, -0.075000f, 0.560000f,
+ -1.000000f, -0.075000f, -0.000000f,
+ -0.560000f, -0.300000f, 1.000000f,
+ -1.000000f, -0.300000f, 0.560000f,
+ -1.000000f, -0.300000f, -0.000000f,
+ -0.875000f, 0.187500f, -0.490000f,
+ -0.490000f, 0.187500f, -0.875000f,
+ 0.000000f, 0.187500f, -0.875000f,
+ -1.000000f, -0.075000f, -0.560000f,
+ -0.560000f, -0.075000f, -1.000000f,
+ 0.000000f, -0.075000f, -1.000000f,
+ -1.000000f, -0.300000f, -0.560000f,
+ -0.560000f, -0.300000f, -1.000000f,
+ 0.000000f, -0.300000f, -1.000000f,
+ 0.490000f, 0.187500f, -0.875000f,
+ 0.875000f, 0.187500f, -0.490000f,
+ 0.560000f, -0.075000f, -1.000000f,
+ 1.000000f, -0.075000f, -0.560000f,
+ 0.560000f, -0.300000f, -1.000000f,
+ 1.000000f, -0.300000f, -0.560000f,
+ 1.000000f, -0.525000f, -0.000000f,
+ 1.000000f, -0.525000f, 0.560000f,
+ 0.560000f, -0.525000f, 1.000000f,
+ 0.000000f, -0.525000f, 1.000000f,
+ 0.750000f, -0.637500f, -0.000000f,
+ 0.750000f, -0.637500f, 0.420000f,
+ 0.420000f, -0.637500f, 0.750000f,
+ 0.000000f, -0.637500f, 0.750000f,
+ 0.750000f, -0.675000f, -0.000000f,
+ 0.750000f, -0.675000f, 0.420000f,
+ 0.420000f, -0.675000f, 0.750000f,
+ 0.000000f, -0.675000f, 0.750000f,
+ -0.560000f, -0.525000f, 1.000000f,
+ -1.000000f, -0.525000f, 0.560000f,
+ -1.000000f, -0.525000f, -0.000000f,
+ -0.420000f, -0.637500f, 0.750000f,
+ -0.750000f, -0.637500f, 0.420000f,
+ -0.750000f, -0.637500f, -0.000000f,
+ -0.420000f, -0.675000f, 0.750000f,
+ -0.750000f, -0.675000f, 0.420000f,
+ -0.750000f, -0.675000f, -0.000000f,
+ -1.000000f, -0.525000f, -0.560000f,
+ -0.560000f, -0.525000f, -1.000000f,
+ 0.000000f, -0.525000f, -1.000000f,
+ -0.750000f, -0.637500f, -0.420000f,
+ -0.420000f, -0.637500f, -0.750000f,
+ 0.000000f, -0.637500f, -0.750000f,
+ -0.750000f, -0.675000f, -0.420000f,
+ -0.420000f, -0.675000f, -0.750000f,
+ 0.000000f, -0.675000f, -0.750000f,
+ 0.560000f, -0.525000f, -1.000000f,
+ 1.000000f, -0.525000f, -0.560000f,
+ 0.420000f, -0.637500f, -0.750000f,
+ 0.750000f, -0.637500f, -0.420000f,
+ 0.420000f, -0.675000f, -0.750000f,
+ 0.750000f, -0.675000f, -0.420000f,
+ -0.800000f, 0.262500f, -0.000000f,
+ -0.800000f, 0.262500f, 0.150000f,
+ -0.750000f, 0.375000f, 0.150000f,
+ -0.750000f, 0.375000f, -0.000000f,
+ -1.150000f, 0.262500f, -0.000000f,
+ -1.150000f, 0.262500f, 0.150000f,
+ -1.250000f, 0.375000f, 0.150000f,
+ -1.250000f, 0.375000f, -0.000000f,
+ -1.350000f, 0.262500f, -0.000000f,
+ -1.350000f, 0.262500f, 0.150000f,
+ -1.500000f, 0.375000f, 0.150000f,
+ -1.500000f, 0.375000f, -0.000000f,
+ -1.350000f, 0.150000f, -0.000000f,
+ -1.350000f, 0.150000f, 0.150000f,
+ -1.500000f, 0.150000f, 0.150000f,
+ -1.500000f, 0.150000f, -0.000000f,
+ -0.750000f, 0.375000f, -0.150000f,
+ -0.800000f, 0.262500f, -0.150000f,
+ -1.250000f, 0.375000f, -0.150000f,
+ -1.150000f, 0.262500f, -0.150000f,
+ -1.500000f, 0.375000f, -0.150000f,
+ -1.350000f, 0.262500f, -0.150000f,
+ -1.500000f, 0.150000f, -0.150000f,
+ -1.350000f, 0.150000f, -0.150000f,
+ -1.350000f, 0.037500f, -0.000000f,
+ -1.350000f, 0.037500f, 0.150000f,
+ -1.500000f, -0.075000f, 0.150000f,
+ -1.500000f, -0.075000f, -0.000000f,
+ -1.250000f, -0.187500f, -0.000000f,
+ -1.250000f, -0.187500f, 0.150000f,
+ -1.325000f, -0.281250f, 0.150000f,
+ -1.325000f, -0.281250f, -0.000000f,
+ -1.000000f, -0.300000f, 0.150000f,
+ -0.950000f, -0.450000f, 0.150000f,
+ -0.950000f, -0.450000f, -0.000000f,
+ -1.500000f, -0.075000f, -0.150000f,
+ -1.350000f, 0.037500f, -0.150000f,
+ -1.325000f, -0.281250f, -0.150000f,
+ -1.250000f, -0.187500f, -0.150000f,
+ -0.950000f, -0.450000f, -0.150000f,
+ -1.000000f, -0.300000f, -0.150000f,
+ 0.850000f, -0.037500f, -0.000000f,
+ 0.850000f, -0.037500f, 0.330000f,
+ 0.850000f, -0.450000f, 0.330000f,
+ 0.850000f, -0.450000f, -0.000000f,
+ 1.300000f, -0.037500f, -0.000000f,
+ 1.300000f, -0.037500f, 0.330000f,
+ 1.550000f, -0.337500f, 0.330000f,
+ 1.550000f, -0.337500f, -0.000000f,
+ 1.150000f, 0.300000f, -0.000000f,
+ 1.150000f, 0.300000f, 0.125000f,
+ 1.200000f, 0.262500f, 0.125000f,
+ 1.200000f, 0.262500f, -0.000000f,
+ 1.350000f, 0.450000f, -0.000000f,
+ 1.350000f, 0.450000f, 0.125000f,
+ 1.650000f, 0.450000f, 0.125000f,
+ 1.650000f, 0.450000f, -0.000000f,
+ 0.850000f, -0.450000f, -0.330000f,
+ 0.850000f, -0.037500f, -0.330000f,
+ 1.550000f, -0.337500f, -0.330000f,
+ 1.300000f, -0.037500f, -0.330000f,
+ 1.200000f, 0.262500f, -0.125000f,
+ 1.150000f, 0.300000f, -0.125000f,
+ 1.650000f, 0.450000f, -0.125000f,
+ 1.350000f, 0.450000f, -0.125000f,
+ 1.400000f, 0.487500f, -0.000000f,
+ 1.400000f, 0.487500f, 0.125000f,
+ 1.762500f, 0.496875f, 0.125000f,
+ 1.762500f, 0.496875f, -0.000000f,
+ 1.450000f, 0.487500f, -0.000000f,
+ 1.450000f, 0.487500f, 0.075000f,
+ 1.725000f, 0.506250f, 0.075000f,
+ 1.725000f, 0.506250f, -0.000000f,
+ 1.400000f, 0.450000f, -0.000000f,
+ 1.400000f, 0.450000f, 0.075000f,
+ 1.600000f, 0.450000f, 0.075000f,
+ 1.600000f, 0.450000f, -0.000000f,
+ 1.762500f, 0.496875f, -0.125000f,
+ 1.400000f, 0.487500f, -0.125000f,
+ 1.725000f, 0.506250f, -0.075000f,
+ 1.450000f, 0.487500f, -0.075000f,
+ 1.600000f, 0.450000f, -0.075000f,
+ 1.400000f, 0.450000f, -0.075000f,
+ 0.000000f, 0.825000f, -0.000000f,
+ 0.000000f, 0.825000f, 0.001000f,
+ 0.001000f, 0.825000f, -0.000000f,
+ 0.400000f, 0.825000f, -0.000000f,
+ 0.400000f, 0.825000f, 0.225000f,
+ 0.225000f, 0.825000f, 0.400000f,
+ 0.000000f, 0.825000f, 0.400000f,
+ 0.000000f, 0.675000f, -0.000000f,
+ 0.100000f, 0.600000f, -0.000000f,
+ 0.100000f, 0.600000f, 0.056000f,
+ 0.056000f, 0.600000f, 0.100000f,
+ 0.000000f, 0.600000f, 0.100000f,
+ -0.001000f, 0.825000f, -0.000000f,
+ -0.225000f, 0.825000f, 0.400000f,
+ -0.400000f, 0.825000f, 0.225000f,
+ -0.400000f, 0.825000f, -0.000000f,
+ -0.056000f, 0.600000f, 0.100000f,
+ -0.100000f, 0.600000f, 0.056000f,
+ -0.100000f, 0.600000f, -0.000000f,
+ 0.000000f, 0.825000f, -0.001000f,
+ -0.400000f, 0.825000f, -0.225000f,
+ -0.225000f, 0.825000f, -0.400000f,
+ 0.000000f, 0.825000f, -0.400000f,
+ -0.100000f, 0.600000f, -0.056000f,
+ -0.056000f, 0.600000f, -0.100000f,
+ 0.000000f, 0.600000f, -0.100000f,
+ 0.225000f, 0.825000f, -0.400000f,
+ 0.400000f, 0.825000f, -0.225000f,
+ 0.056000f, 0.600000f, -0.100000f,
+ 0.100000f, 0.600000f, -0.056000f,
+ 0.200000f, 0.525000f, -0.000000f,
+ 0.200000f, 0.525000f, 0.112000f,
+ 0.112000f, 0.525000f, 0.200000f,
+ 0.000000f, 0.525000f, 0.200000f,
+ 0.650000f, 0.525000f, -0.000000f,
+ 0.650000f, 0.525000f, 0.364000f,
+ 0.364000f, 0.525000f, 0.650000f,
+ 0.000000f, 0.525000f, 0.650000f,
+ 0.650000f, 0.450000f, -0.000000f,
+ 0.650000f, 0.450000f, 0.364000f,
+ 0.364000f, 0.450000f, 0.650000f,
+ 0.000000f, 0.450000f, 0.650000f,
+ -0.112000f, 0.525000f, 0.200000f,
+ -0.200000f, 0.525000f, 0.112000f,
+ -0.200000f, 0.525000f, -0.000000f,
+ -0.364000f, 0.525000f, 0.650000f,
+ -0.650000f, 0.525000f, 0.364000f,
+ -0.650000f, 0.525000f, -0.000000f,
+ -0.364000f, 0.450000f, 0.650000f,
+ -0.650000f, 0.450000f, 0.364000f,
+ -0.650000f, 0.450000f, -0.000000f,
+ -0.200000f, 0.525000f, -0.112000f,
+ -0.112000f, 0.525000f, -0.200000f,
+ 0.000000f, 0.525000f, -0.200000f,
+ -0.650000f, 0.525000f, -0.364000f,
+ -0.364000f, 0.525000f, -0.650000f,
+ 0.000000f, 0.525000f, -0.650000f,
+ -0.650000f, 0.450000f, -0.364000f,
+ -0.364000f, 0.450000f, -0.650000f,
+ 0.000000f, 0.450000f, -0.650000f,
+ 0.112000f, 0.525000f, -0.200000f,
+ 0.200000f, 0.525000f, -0.112000f,
+ 0.364000f, 0.525000f, -0.650000f,
+ 0.650000f, 0.525000f, -0.364000f,
+ 0.364000f, 0.450000f, -0.650000f,
+ 0.650000f, 0.450000f, -0.364000f,
+ 0.000000f, -0.750000f, -0.000000f,
+ 0.750000f, -0.675000f, -0.000000f,
+ 0.750000f, -0.675000f, -0.420000f,
+ 0.420000f, -0.675000f, -0.750000f,
+ 0.000000f, -0.675000f, -0.750000f,
+ 0.750000f, -0.712500f, -0.000000f,
+ 0.750000f, -0.712500f, -0.420000f,
+ 0.420000f, -0.712500f, -0.750000f,
+ 0.000000f, -0.712500f, -0.750000f,
+ 0.712500f, -0.750000f, -0.000000f,
+ 0.712500f, -0.750000f, -0.399000f,
+ 0.399000f, -0.750000f, -0.712500f,
+ 0.000000f, -0.750000f, -0.712500f,
+ -0.420000f, -0.675000f, -0.750000f,
+ -0.750000f, -0.675000f, -0.420000f,
+ -0.750000f, -0.675000f, -0.000000f,
+ -0.420000f, -0.712500f, -0.750000f,
+ -0.750000f, -0.712500f, -0.420000f,
+ -0.750000f, -0.712500f, -0.000000f,
+ -0.399000f, -0.750000f, -0.712500f,
+ -0.712500f, -0.750000f, -0.399000f,
+ -0.712500f, -0.750000f, -0.000000f,
+ -0.750000f, -0.675000f, 0.420000f,
+ -0.420000f, -0.675000f, 0.750000f,
+ 0.000000f, -0.675000f, 0.750000f,
+ -0.750000f, -0.712500f, 0.420000f,
+ -0.420000f, -0.712500f, 0.750000f,
+ 0.000000f, -0.712500f, 0.750000f,
+ -0.712500f, -0.750000f, 0.399000f,
+ -0.399000f, -0.750000f, 0.712500f,
+ 0.000000f, -0.750000f, 0.712500f,
+ 0.420000f, -0.675000f, 0.750000f,
+ 0.750000f, -0.675000f, 0.420000f,
+ 0.420000f, -0.712500f, 0.750000f,
+ 0.750000f, -0.712500f, 0.420000f,
+ 0.399000f, -0.750000f, 0.712500f,
+ 0.712500f, -0.750000f, 0.399000f
+};
+
+static ushort const teapotPatchData[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 3, 16, 17, 18, 7, 19, 20, 21, 11, 22, 23, 24, 15, 25, 26, 27,
+ 18, 28, 29, 30, 21, 31, 32, 33, 24, 34, 35, 36, 27, 37, 38, 39,
+ 30, 40, 41, 0, 33, 42, 43, 4, 36, 44, 45, 8, 39, 46, 47, 12,
+ 12, 13, 14, 15, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 15, 25, 26, 27, 51, 60, 61, 62, 55, 63, 64, 65, 59, 66, 67, 68,
+ 27, 37, 38, 39, 62, 69, 70, 71, 65, 72, 73, 74, 68, 75, 76, 77,
+ 39, 46, 47, 12, 71, 78, 79, 48, 74, 80, 81, 52, 77, 82, 83, 56,
+ 56, 57, 58, 59, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 59, 66, 67, 68, 87, 96, 97, 98, 91, 99, 100, 101, 95, 102, 103, 104,
+ 68, 75, 76, 77, 98, 105, 106, 107, 101, 108, 109, 110, 104, 111, 112, 113,
+ 77, 82, 83, 56, 107, 114, 115, 84, 110, 116, 117, 88, 113, 118, 119, 92,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 123, 136, 137, 120, 127, 138, 139, 124, 131, 140, 141, 128, 135, 142, 143, 132,
+ 132, 133, 134, 135, 144, 145, 146, 147, 148, 149, 150, 151, 68, 152, 153, 154,
+ 135, 142, 143, 132, 147, 155, 156, 144, 151, 157, 158, 148, 154, 159, 160, 68,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 164, 177, 178, 161, 168, 179, 180, 165, 172, 181, 182, 169, 176, 183, 184, 173,
+ 173, 174, 175, 176, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 176, 183, 184, 173, 188, 197, 198, 185, 192, 199, 200, 189, 196, 201, 202, 193,
+ 203, 203, 203, 203, 206, 207, 208, 209, 210, 210, 210, 210, 211, 212, 213, 214,
+ 203, 203, 203, 203, 209, 216, 217, 218, 210, 210, 210, 210, 214, 219, 220, 221,
+ 203, 203, 203, 203, 218, 223, 224, 225, 210, 210, 210, 210, 221, 226, 227, 228,
+ 203, 203, 203, 203, 225, 229, 230, 206, 210, 210, 210, 210, 228, 231, 232, 211,
+ 211, 212, 213, 214, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 214, 219, 220, 221, 236, 245, 246, 247, 240, 248, 249, 250, 244, 251, 252, 253,
+ 221, 226, 227, 228, 247, 254, 255, 256, 250, 257, 258, 259, 253, 260, 261, 262,
+ 228, 231, 232, 211, 256, 263, 264, 233, 259, 265, 266, 237, 262, 267, 268, 241,
+ 269, 269, 269, 269, 278, 279, 280, 281, 274, 275, 276, 277, 270, 271, 272, 273,
+ 269, 269, 269, 269, 281, 288, 289, 290, 277, 285, 286, 287, 273, 282, 283, 284,
+ 269, 269, 269, 269, 290, 297, 298, 299, 287, 294, 295, 296, 284, 291, 292, 293,
+ 269, 269, 269, 269, 299, 304, 305, 278, 296, 302, 303, 274, 293, 300, 301, 270
+};
+
+#endif
diff --git a/src/threed/geometry/qlogicalvertex.cpp b/src/threed/geometry/qlogicalvertex.cpp
new file mode 100644
index 000000000..e1f892c3d
--- /dev/null
+++ b/src/threed/geometry/qlogicalvertex.cpp
@@ -0,0 +1,416 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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 "qlogicalvertex.h"
+#include "qvector_utils_p.h"
+
+#include <QtCore/qdebug.h>
+
+/*!
+ \class QLogicalVertex
+ \brief The QLogicalVertex class references QGeometryData at a single vertex.
+ \since 4.8
+ \ingroup qt3d
+ \ingroup qt3d::geometry
+
+ QLogicalVertex instances are a convenience class for use with
+ QGeometryData. A QLogicalVertex simply references through to the data
+ in a QGeometryData for a particular vertex, providing accessors to fetch
+ position, texture coordinates, and other values.
+
+ Create a QLogicalVertex referring to a particular QGeometryData instance:
+ \code
+ QGeometryData data;
+ data.appendVertex(QVector3D(1, 2, 3));
+
+ // construct a QLogicalVertex referring to the first vertex in data
+ // the QGeometryData is implicitly shared with lv
+ QLogicalVertex lv(data, 0);
+ // lv.vertex() == QVector3D(1, 2, 3)
+ \endcode
+ This is inexpensive and no new storage is allocated for the actual data,
+ just the reference and index.
+
+ With logical vertices instances referencing large QGeometryData instances,
+ avoid modifying the instance:
+ \code
+ // careful - assigning to a QLogicalVertex which refers to an external
+ // QGeometryData will result in a possibly expensive copy-on-write
+ lv.setVertex(3, 2, 1);
+ \endcode
+
+ Create a QLogicalVertex with its own QGeometryData internally:
+ \code
+ QLogicalVertex lv;
+ // no copy on write here - internal QGeometryData is not shared
+ lv.setVertex(1, 2, 3);
+ \endcode
+
+ Assign an instance of QLogicalVertex:
+ \code
+ QLogicalVertex lv2;
+ lv2 = data.logicalVertexAt(0);
+ \endcode
+ Although lv2 gets its own internal QGeometryData which is then immediately
+ thrown away by the assignment, because of lazy initialization in
+ QGeometryData the cost is negligible.
+
+ Use the fields() and hasField() functions to determine if a particular
+ field is present in the vertex. Accessing non-existent data will cause
+ an assert in debug mode (from the underlying QArray), and give
+ undefined behaviour in release mode.
+
+ \sa QGeometryData, QGLBuilder
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex()
+ Constructs a new invalid QLogicalVertex which has no data.
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex(QGeometryData data, int index)
+ Constructs a new QLogicalVertex referencing the \a data at \a index.
+ Note that if this QLogicalVertex is modified, by calling vertex() or
+ setNormal() for example, then a copy-on-write for \a data will be
+ triggered.
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex(const QVector3D &a)
+ Constructs a new QLogicalVertex with a vertex set to \a a.
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n);
+ Constructs a new QLogicalVertex with a vertex set to \a a, and normal set to \a n.
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t)
+ Constructs a new QLogicalVertex with its vertex value set to \a a, normal set
+ to \a n, and texture set to \a t. By default \a n is the null QVector3D,
+ and \a t is the InvalidTexCoord. If \a n is null then hasType(QLogicalVertex::Normal)
+ will return false. Likewise if \a t is the InvalidTexCoord then
+ hasType(QLogicalVertex::Texture) will return false.
+*/
+
+/*!
+ \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, QColor4ub color)
+ Constructs a new QLogicalVertex with its vertex value set to \a a,
+ color value set to \a color.
+*/
+
+/*!
+ \fn QLogicalVertex::~QLogicalVertex()
+ Destroys this QLogicalVertex reclaiming any resources.
+*/
+
+/*!
+ \fn const QVector3D &QLogicalVertex::vertex() const
+ Returns a const reference to the vertex value for this vertex.
+*/
+
+/*!
+ \fn void QLogicalVertex::setVertex(const QVector3D &v)
+ Sets the vertex value for this vertex to \a v.
+*/
+
+/*!
+ \fn QVector3D &QLogicalVertex::vertex()
+ Returns a modifiable reference to the vertex value.
+*/
+
+/*!
+ \fn QLogicalVertex::operator QVector3D ()
+ Returns a copy of the vertex value, by casting as a QVector3D. This
+ allows passing of a QLogicalVertex to functions that expect a QVector3D.
+*/
+
+/*!
+ \fn QVariant QLogicalVertex::attribute(QGL::VertexAttribute field) const
+ Returns the attribute value for \a field. The \a field defaults
+ to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn void QLogicalVertex::setAttribute(float value, QGL::VertexAttribute field)
+ Sets the float attribute \a value at \a field. The \a field
+ defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn void QLogicalVertex::setAttribute(const QVector2D &v, QGL::VertexAttribute field)
+ Sets the QVector2D attribute \a v at \a field. The \a field
+ defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn void QLogicalVertex::setAttribute(const QVector3D &v, QGL::VertexAttribute field)
+ Sets the QVector3D attribute \a v at \a field. The \a field
+ defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn float &QLogicalVertex::floatAttribute(QGL::VertexAttribute field)
+ Returns a modifiable reference to the attribute at \a field, which
+ must be a float. The \a field defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn QVector2D &QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field)
+ Returns a modifiable reference to the attribute at \a field, which
+ must be a QVector2D. The \a field defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn QVector3D &QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0);
+ Returns a modifiable reference to the attribute at \a field, which
+ must be a QVector3D. The \a field defaults to QGL::CustomVertex0.
+*/
+
+/*!
+ \fn float QLogicalVertex::floatAttribute(QGL::VertexAttribute field) const
+ Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0.
+ The attribute must be a float value.
+*/
+
+/*!
+ \fn QVector2D QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) const
+ Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0.
+ The attribute must be a QVector2D value.
+*/
+
+/*!
+ \fn QVector3D QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field) const
+ Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0.
+ The attribute must be a QVector3D value.
+*/
+
+/*!
+ \fn QCustomDataArray::ElementType QLogicalVertex::attributeType(QGL::VertexAttribute field)
+ Returns the element type for the attribute \a field.
+*/
+
+/*!
+ \fn const QVector3D &QLogicalVertex::normal() const
+ Returns a const reference to the normal value for this vertex.
+*/
+
+/*!
+ \fn void QLogicalVertex::setNormal(const QVector3D &n)
+ Sets the normal value for this vertex to \a n.
+*/
+
+/*!
+ \fn QVector3D &QLogicalVertex::normal()
+ Returns a modifiable reference to the normal value for this vertex.
+*/
+
+/*!
+ \fn QVector2D QLogicalVertex::texCoord(QGL::VertexAttribute field) const
+ Returns a copy of the texture coordinate value at \a field for this vertex.
+ The \a field defaults to QGL::TextureCoord0.
+*/
+
+/*!
+ \fn void QLogicalVertex::setTexCoord(const QVector2D &t, QGL::VertexAttribute field)
+ Sets the texture coordinate at \a field for this vertex to \a t.
+ The \a field defaults to QGL::TextureCoord0.
+*/
+
+/*!
+ \fn QVector2D &QLogicalVertex::texCoordRef(QGL::VertexAttribute field)
+ Returns a modifiable reference to the texture coordinate for this vertex.
+ The \a field defaults to QGL::TextureCoord0.
+*/
+
+/*!
+ \fn QColor4ub QLogicalVertex::color() const
+ Returns a const reference to the color value for this vertex.
+*/
+
+/*!
+ \fn void QLogicalVertex::setColor(const QColor4ub &c)
+ Sets the color value for this vertex to \a c.
+*/
+
+/*!
+ \fn QColor4ub &QLogicalVertex::colorRef()
+ Returns a modifiable reference to the color value for this vertex.
+*/
+
+/*!
+ \fn bool QLogicalVertex::hasField(QGL::VertexAttribute type) const
+ Returns true if this vertex has data field \a type, and false otherwise.
+
+ In general check to see if a logical vertex has a particular field
+ type before attempting to access it. In debug mode accessing a
+ non-existent field will cause an assert; but in release mode the
+ behaviour is undefined.
+*/
+
+/*!
+ \fn quint32 QLogicalVertex::fields() const
+ Returns a bit-mask of the fields in this logical vertex. Test the
+ fields like this:
+ \code
+ if (vertex.fields() & QGL::fieldMask(QGL::TextureCoord0))
+ tex = vertex.texCoord();
+ \endcode
+
+ \sa QGeometryData::fields()
+*/
+
+/*!
+ \fn int QLogicalVertex::index() const
+ Returns the index at which this logical vertex's data is located in
+ its associated QGeometryData; or -1 if this vertex is null.
+*/
+
+/*!
+ \fn QGeometryData QLogicalVertex::data() const
+ Returns a copy of the QGeometryData underlying this vertex. Note that
+ the copy is not expensive in terms of performance due to implicit sharing
+ unless the copy is modified (causing a copy-on-write).
+
+ \sa QLogicalVertex::index()
+*/
+
+/*!
+ \fn QGeometryData data() const
+ Returns a copy of the QGeometryData associated with this vertex. Note
+ that as long as the copy is not modified, this method is not expensive.
+*/
+
+/*!
+ \fn bool QLogicalVertex::isNull() const
+ Returns true if this vertex is null.
+
+ \sa QLogicalVertex()
+*/
+
+/*!
+ Returns true if \a rhs has exactly the same fields as this logical
+ vertex, and each of those are equal to the corresponding field of the \a rhs.
+
+ If either are null, then false is returned.
+*/
+bool QLogicalVertex::operator==(const QLogicalVertex &rhs) const
+{
+ if (isNull() || rhs.isNull())
+ return false;
+ if (this == &rhs)
+ return true;
+ if (m_data.fields() != rhs.fields())
+ return false;
+ const quint32 mask = 0x01;
+ quint32 fields = m_data.fields();
+ for (int field = 0; fields; ++field, fields >>= 1)
+ {
+ if (mask & fields)
+ {
+ QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field);
+ if (attr < QGL::TextureCoord0)
+ {
+ if (attr == QGL::Position)
+ {
+ if (!qFskCompare(vertex(), rhs.vertex()))
+ return false;
+ }
+ else if (attr == QGL::Normal)
+ {
+ if (!qFskCompare(normal(), rhs.normal()))
+ return false;
+ }
+ else
+ {
+ if (color() != rhs.color())
+ return false;
+ }
+ }
+ else if (attr < QGL::CustomVertex0)
+ {
+ if (!qFskCompare(texCoord(attr), rhs.texCoord(attr)))
+ return false;
+ }
+ else
+ {
+ if (attribute(attr) != rhs.attribute(attr))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QLogicalVertex &lv)
+{
+ dbg.nospace();
+ dbg << "QLogicalVertex(";
+ if (lv.isNull())
+ {
+ dbg << " NULL";
+ }
+ else
+ {
+ if (lv.hasField(QGL::Position))
+ dbg << "V:" << QVector3D(lv.vertex());
+ else
+ dbg << " (No Vertex)";
+ if (lv.hasField(QGL::Normal))
+ dbg << "N:" << QVector3D(lv.normal());
+ else
+ dbg << " (No Normal)";
+ if (lv.hasField(QGL::TextureCoord0))
+ dbg << "T:" << QVector2D(lv.texCoord());
+ else
+ dbg << " (No Texture)";
+ if (lv.hasField(QGL::Color))
+ dbg << "C:" << QColor4ub(lv.color());
+ else
+ dbg << " (No Color)";
+ }
+ dbg << " )";
+ return dbg.space();
+}
+#endif
diff --git a/src/threed/geometry/qlogicalvertex.h b/src/threed/geometry/qlogicalvertex.h
new file mode 100644
index 000000000..304aa9b05
--- /dev/null
+++ b/src/threed/geometry/qlogicalvertex.h
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QLOGICALVERTEX_H
+#define QLOGICALVERTEX_H
+
+#include "qgeometrydata.h"
+#include "qcustomdataarray.h"
+
+QT_BEGIN_NAMESPACE
+
+// uncomment this to perform heavy checking of QLogicalVertex
+// #define QT3D_DEBUG_QLOGICALVERTEX 1
+
+class QLogicalVertex
+{
+public:
+ inline QLogicalVertex();
+ inline QLogicalVertex(QGeometryData data, int index);
+ inline QLogicalVertex(const QVector3D &a);
+ inline QLogicalVertex(const QVector3D &a, const QVector3D &n);
+ inline QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t);
+ inline QLogicalVertex(const QVector3D &a, QColor4ub color);
+ ~QLogicalVertex() {}
+
+ inline const QVector3D &vertex() const;
+ inline void setVertex(const QVector3D &v);
+ inline QVector3D &vertex();
+
+ operator QVector3D () { return vertex(); }
+
+ inline QVariant attribute(QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ inline void setAttribute(float value, QGL::VertexAttribute attr);
+ inline void setAttribute(const QVector2D &v, QGL::VertexAttribute field = QGL::CustomVertex0);
+ inline void setAttribute(const QVector3D &v, QGL::VertexAttribute field = QGL::CustomVertex0);
+ inline float &floatAttribute(QGL::VertexAttribute field = QGL::CustomVertex0);
+ inline QVector2D &vector2DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0);
+ inline QVector3D &vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0);
+ inline float floatAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ inline QVector2D vector2DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ inline QVector3D vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const;
+ inline QCustomDataArray::ElementType attributeType(QGL::VertexAttribute field = QGL::CustomVertex0);
+
+ inline const QVector3D &normal() const;
+ inline void setNormal(const QVector3D &n);
+ inline QVector3D &normal();
+
+ inline const QColor4ub &color() const;
+ inline void setColor(const QColor4ub &c);
+ inline QColor4ub &colorRef();
+
+ inline const QVector2D &texCoord(QGL::VertexAttribute attr = QGL::TextureCoord0) const;
+ inline void setTexCoord(const QVector2D &t, QGL::VertexAttribute attr = QGL::TextureCoord0);
+ inline QVector2D &texCoordRef(QGL::VertexAttribute attr = QGL::TextureCoord0);
+
+ inline bool hasField(QGL::VertexAttribute type) const;
+ inline quint32 fields() const;
+ inline int index() const;
+ inline QGeometryData data() const;
+ inline bool isNull() const;
+
+ bool operator==(const QLogicalVertex &rhs) const;
+
+private:
+ QGeometryData m_data;
+ int m_index;
+};
+
+inline QLogicalVertex::QLogicalVertex()
+ : m_index(-1)
+{
+}
+
+inline QLogicalVertex::QLogicalVertex(QGeometryData data, int index)
+ : m_data(data)
+ , m_index(index)
+{
+ Q_ASSERT(index < data.count());
+#ifdef QT3D_DEBUG_QLOGICALVERTEX
+ data.check();
+#endif
+}
+
+inline QLogicalVertex::QLogicalVertex(const QVector3D &a)
+ : m_index(0)
+{
+ m_data.appendVertex(a);
+}
+
+inline QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n)
+ : m_index(0)
+{
+ m_data.appendVertex(a);
+ m_data.appendNormal(n);
+}
+
+inline QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t)
+ : m_index(0)
+{
+ m_data.appendVertex(a);
+ m_data.appendNormal(n);
+ m_data.appendTexCoord(t);
+}
+
+inline QLogicalVertex::QLogicalVertex(const QVector3D &a, QColor4ub color)
+ : m_index(0)
+{
+ m_data.appendVertex(a);
+ m_data.appendColor(color);
+}
+
+inline const QVector3D &QLogicalVertex::vertex() const
+{
+ return m_data.vertexAt(m_index);
+}
+
+inline void QLogicalVertex::setVertex(const QVector3D &v)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(QGL::Position))
+ m_data.appendVertex(v);
+ else
+ m_data.vertex(m_index) = v;
+}
+
+inline QVector3D &QLogicalVertex::vertex()
+{
+ return m_data.vertex(m_index);
+}
+
+inline QVariant QLogicalVertex::attribute(QGL::VertexAttribute attr) const
+{
+ return m_data.attributes(attr).at(m_index);
+}
+
+inline void QLogicalVertex::setAttribute(float v, QGL::VertexAttribute attr)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(attr))
+ m_data.appendAttribute(v, attr);
+ else
+ m_data.floatAttribute(m_index, attr) = v;
+}
+
+inline void QLogicalVertex::setAttribute(const QVector2D &v, QGL::VertexAttribute attr)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(attr))
+ m_data.appendAttribute(v, attr);
+ else
+ m_data.vector2DAttribute(m_index, attr) = v;
+}
+
+inline void QLogicalVertex::setAttribute(const QVector3D &v, QGL::VertexAttribute attr)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(attr))
+ m_data.appendAttribute(v, attr);
+ else
+ m_data.vector3DAttribute(m_index, attr) = v;
+}
+
+inline float &QLogicalVertex::floatAttribute(QGL::VertexAttribute field)
+{
+ return m_data.floatAttribute(m_index, field);
+}
+
+inline QVector2D &QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field)
+{
+ return m_data.vector2DAttribute(m_index, field);
+}
+
+inline QVector3D &QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field)
+{
+
+ return m_data.vector3DAttribute(m_index, field);
+}
+
+inline float QLogicalVertex::floatAttribute(QGL::VertexAttribute field) const
+{
+ return m_data.floatAttributeAt(m_index, field);
+}
+
+inline QVector2D QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) const
+{
+ return m_data.vector2DAttributeAt(m_index, field);
+}
+
+inline QVector3D QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field) const
+{
+ return m_data.vector3DAttributeAt(m_index, field);
+}
+
+inline QCustomDataArray::ElementType QLogicalVertex::attributeType(QGL::VertexAttribute field)
+{
+ return m_data.attributes(field).elementType();
+}
+
+inline const QVector3D &QLogicalVertex::normal() const
+{
+ return m_data.normalAt(m_index);
+}
+
+inline void QLogicalVertex::setNormal(const QVector3D &n)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(QGL::Normal))
+ m_data.appendNormal(n);
+ else
+ m_data.normal(m_index) = n;
+}
+
+inline QVector3D &QLogicalVertex::normal()
+{
+ return m_data.normal(m_index);
+}
+
+inline const QVector2D &QLogicalVertex::texCoord(QGL::VertexAttribute attr) const
+{
+ return m_data.texCoordAt(m_index, attr);
+}
+
+inline void QLogicalVertex::setTexCoord(const QVector2D &t, QGL::VertexAttribute attr)
+{
+ Q_ASSERT(attr >= QGL::TextureCoord0 && attr <= QGL::TextureCoord2);
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(attr))
+ m_data.appendTexCoord(t, attr);
+ else
+ m_data.texCoord(m_index, attr) = t;
+}
+
+inline QVector2D &QLogicalVertex::texCoordRef(QGL::VertexAttribute attr)
+{
+ return m_data.texCoord(m_index, attr);
+}
+
+inline const QColor4ub &QLogicalVertex::color() const
+{
+ return m_data.colorAt(m_index);
+}
+
+inline void QLogicalVertex::setColor(const QColor4ub &c)
+{
+ if (m_index == -1)
+ m_index = 0;
+ if (m_index == m_data.count(QGL::Color))
+ m_data.appendColor(c);
+ else
+ m_data.color(m_index) = c;
+}
+
+inline QColor4ub &QLogicalVertex::colorRef()
+{
+ return m_data.color(m_index);
+}
+
+inline bool QLogicalVertex::hasField(QGL::VertexAttribute attr) const
+{
+ return m_data.hasField(attr);
+}
+
+inline quint32 QLogicalVertex::fields() const
+{
+ return m_data.fields();
+}
+
+inline int QLogicalVertex::index() const
+{
+ return m_index;
+}
+
+inline bool QLogicalVertex::isNull() const
+{
+ return (m_index == -1);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QLogicalVertex &section);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QLOGICALVERTEX_H
diff --git a/src/threed/geometry/qvector_utils_p.h b/src/threed/geometry/qvector_utils_p.h
new file mode 100644
index 000000000..64b436226
--- /dev/null
+++ b/src/threed/geometry/qvector_utils_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtQuick3D module 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$
+**
+****************************************************************************/
+
+#ifndef QVECTOR_UTILS_P_H
+#define QVECTOR_UTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qvector3d.h>
+#include <QtGui/qvector2d.h>
+
+// Replacement for qFuzzyCompare(QVector3D, QVector3D) and friends,
+// for a specific case where the results are going to be rendered
+// by the GPU onto a display.
+//
+// The accuracy of this comparison should not change. Especially it
+// should not change when you go from doubles to floats or when
+// you get close to zero. If two verts or lighting normals are
+// the same to within 5 places of floating point accuracy then they
+// will dislay as being on top of each other.
+//
+// Also this avoids doing 3 floating point multiplications every time
+// since the normal qFuzzyIsNull does a mul to scale the epsilon when
+// close to zero.
+
+inline bool qFskIsNull(double d)
+{
+ return qAbs(d) <= 0.00001;
+}
+
+inline bool qFskIsNull(float f)
+{
+ return qAbs(f) <= 0.00001f;
+}
+
+inline bool qFskCompare(float a, float b)
+{
+ return qFskIsNull(a - b);
+}
+
+inline bool qFskCompare(double a, double b)
+{
+ return qFskIsNull(a - b);
+}
+
+inline bool qFskCompare(const QVector3D &a, const QVector3D &b)
+{
+ return (
+ qFskIsNull(a.x() - b.x()) &&
+ qFskIsNull(a.y() - b.y()) &&
+ qFskIsNull(a.z() - b.z())
+ );
+}
+
+inline bool qFskCompare(const QVector2D &a, const QVector2D &b)
+{
+ return (
+ qFskIsNull(a.x() - b.x()) &&
+ qFskIsNull(a.y() - b.y())
+ );
+}
+
+#endif // QVECTOR_UTILS_P_H