summaryrefslogtreecommitdiffstats
path: root/src/threed/geometry/qgeometrydata.cpp
diff options
context:
space:
mode:
authorSarah Smith <sarah.j.smith@nokia.com>2011-03-09 17:08:07 +1000
committerSarah Smith <sarah.j.smith@nokia.com>2011-03-09 17:43:07 +1000
commitb87a08f5292069facf9b427cb43a675d43578293 (patch)
treeaf3efc8960a3e556e15b7b66f81821b01297c366 /src/threed/geometry/qgeometrydata.cpp
Add files ported from research/qt3d
Fill repo with some of the files from the research/qt3d project. At present the new project builds under MacOSX. To-dos include getting it building under linux, windows, harmattan and maemo/meego; updating the documentation to reflect the new QtQuick focus; and fixing the issues with private headers.
Diffstat (limited to 'src/threed/geometry/qgeometrydata.cpp')
-rw-r--r--src/threed/geometry/qgeometrydata.cpp2025
1 files changed, 2025 insertions, 0 deletions
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