1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qplanegeometry.h"
41#include "qplanegeometry_p.h"
42
43#include <Qt3DRender/qattribute.h>
44#include <Qt3DRender/qbuffer.h>
45#include <Qt3DRender/qbufferdatagenerator.h>
46
47#include <limits>
48
49QT_BEGIN_NAMESPACE
50
51using namespace Qt3DRender;
52
53namespace Qt3DExtras {
54
55namespace {
56
57QByteArray createPlaneVertexData(float w, float h, const QSize &resolution, bool mirrored)
58{
59 Q_ASSERT(w > 0.0f);
60 Q_ASSERT(h > 0.0f);
61 Q_ASSERT(resolution.width() >= 2);
62 Q_ASSERT(resolution.height() >= 2);
63
64 const int nVerts = resolution.width() * resolution.height();
65
66 // Populate a buffer with the interleaved per-vertex data with
67 // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent
68 const quint32 elementSize = 3 + 2 + 3 + 4;
69 const quint32 stride = elementSize * sizeof(float);
70 QByteArray bufferBytes;
71 bufferBytes.resize(size: stride * nVerts);
72 float* fptr = reinterpret_cast<float*>(bufferBytes.data());
73
74 const float x0 = -w / 2.0f;
75 const float z0 = -h / 2.0f;
76 const float dx = w / (resolution.width() - 1);
77 const float dz = h / (resolution.height() - 1);
78 const float du = 1.0 / (resolution.width() - 1);
79 const float dv = 1.0 / (resolution.height() - 1);
80
81 // Iterate over z
82 for (int j = 0; j < resolution.height(); ++j) {
83 const float z = z0 + static_cast<float>(j) * dz;
84 const float v = static_cast<float>(j) * dv;
85
86 // Iterate over x
87 for (int i = 0; i < resolution.width(); ++i) {
88 const float x = x0 + static_cast<float>(i) * dx;
89 const float u = static_cast<float>(i) * du;
90
91 // position
92 *fptr++ = x;
93 *fptr++ = 0.0;
94 *fptr++ = z;
95
96 // texture coordinates
97 *fptr++ = u;
98 *fptr++ = mirrored ? 1.0f - v : v;
99
100 // normal
101 *fptr++ = 0.0f;
102 *fptr++ = 1.0f;
103 *fptr++ = 0.0f;
104
105 // tangent
106 *fptr++ = 1.0f;
107 *fptr++ = 0.0f;
108 *fptr++ = 0.0f;
109 *fptr++ = 1.0f;
110 }
111 }
112
113 return bufferBytes;
114}
115
116QByteArray createPlaneIndexData(const QSize &resolution)
117{
118 // Create the index data. 2 triangles per rectangular face
119 const int faces = 2 * (resolution.width() - 1) * (resolution.height() - 1);
120 const int indices = 3 * faces;
121 Q_ASSERT(indices < std::numeric_limits<quint16>::max());
122 QByteArray indexBytes;
123 indexBytes.resize(size: indices * sizeof(quint16));
124 quint16* indexPtr = reinterpret_cast<quint16*>(indexBytes.data());
125
126 // Iterate over z
127 for (int j = 0; j < resolution.height() - 1; ++j) {
128 const int rowStartIndex = j * resolution.width();
129 const int nextRowStartIndex = (j + 1) * resolution.width();
130
131 // Iterate over x
132 for (int i = 0; i < resolution.width() - 1; ++i) {
133 // Split quad into two triangles
134 *indexPtr++ = rowStartIndex + i;
135 *indexPtr++ = nextRowStartIndex + i;
136 *indexPtr++ = rowStartIndex + i + 1;
137
138 *indexPtr++ = nextRowStartIndex + i;
139 *indexPtr++ = nextRowStartIndex + i + 1;
140 *indexPtr++ = rowStartIndex + i + 1;
141 }
142 }
143
144 return indexBytes;
145}
146
147} // anonymous
148
149class PlaneVertexBufferFunctor : public QBufferDataGenerator
150{
151public:
152 explicit PlaneVertexBufferFunctor(float w, float h, const QSize &resolution, bool mirrored)
153 : m_width(w)
154 , m_height(h)
155 , m_resolution(resolution)
156 , m_mirrored(mirrored)
157 {}
158
159 ~PlaneVertexBufferFunctor() {}
160
161 QByteArray operator()() final
162 {
163 return createPlaneVertexData(w: m_width, h: m_height, resolution: m_resolution, mirrored: m_mirrored);
164 }
165
166 bool operator ==(const QBufferDataGenerator &other) const final
167 {
168 const PlaneVertexBufferFunctor *otherFunctor = functor_cast<PlaneVertexBufferFunctor>(other: &other);
169 if (otherFunctor != nullptr)
170 return (otherFunctor->m_width == m_width &&
171 otherFunctor->m_height == m_height &&
172 otherFunctor->m_resolution == m_resolution &&
173 otherFunctor->m_mirrored == m_mirrored);
174 return false;
175 }
176
177 QT3D_FUNCTOR(PlaneVertexBufferFunctor)
178
179 private:
180 float m_width;
181 float m_height;
182 QSize m_resolution;
183 bool m_mirrored;
184};
185
186class PlaneIndexBufferFunctor : public QBufferDataGenerator
187{
188public:
189 explicit PlaneIndexBufferFunctor(const QSize &resolution)
190 : m_resolution(resolution)
191 {}
192
193 ~PlaneIndexBufferFunctor() {}
194
195 QByteArray operator()() final
196 {
197 return createPlaneIndexData(resolution: m_resolution);
198 }
199
200 bool operator ==(const QBufferDataGenerator &other) const final
201 {
202 const PlaneIndexBufferFunctor *otherFunctor = functor_cast<PlaneIndexBufferFunctor>(other: &other);
203 if (otherFunctor != nullptr)
204 return (otherFunctor->m_resolution == m_resolution);
205 return false;
206 }
207
208 QT3D_FUNCTOR(PlaneIndexBufferFunctor)
209
210 private:
211 QSize m_resolution;
212};
213
214/*!
215 * \qmltype PlaneGeometry
216 * \instantiates Qt3DExtras::QPlaneGeometry
217 * \inqmlmodule Qt3D.Extras
218 * \brief PlaneGeometry allows creation of a plane in 3D space.
219 *
220 * The PlaneGeometry type is most commonly used internally by the PlaneMesh type
221 * but can also be used in custom GeometryRenderer types.
222 */
223
224/*!
225 * \qmlproperty real PlaneGeometry::width
226 *
227 * Holds the plane width.
228 */
229
230/*!
231 * \qmlproperty real PlaneGeometry::height
232 *
233 * Holds the plane height.
234 */
235
236/*!
237 * \qmlproperty size PlaneGeometry::resolution
238 *
239 * Holds the plane resolution.
240 */
241
242/*!
243 * \qmlproperty bool PlaneGeometry::mirrored
244 * \since 5.9
245 *
246 * Controls if the UV coordinates of the plane should be flipped vertically.
247 */
248
249/*!
250 * \qmlproperty Attribute PlaneGeometry::positionAttribute
251 *
252 * Holds the geometry position attribute.
253 */
254
255/*!
256 * \qmlproperty Attribute PlaneGeometry::normalAttribute
257 *
258 * Holds the geometry normal attribute.
259 */
260
261/*!
262 * \qmlproperty Attribute PlaneGeometry::texCoordAttribute
263 *
264 * Holds the geometry texture coordinate attribute.
265 */
266
267/*!
268 * \qmlproperty Attribute PlaneGeometry::tangentAttribute
269 *
270 * Holds the geometry tangent attribute.
271 */
272
273/*!
274 * \qmlproperty Attribute PlaneGeometry::indexAttribute
275 *
276 * Holds the geometry index attribute.
277 */
278
279/*!
280 * \class Qt3DExtras::QPlaneGeometry
281 \ingroup qt3d-extras-geometries
282 * \inheaderfile Qt3DExtras/QPlaneGeometry
283 * \inmodule Qt3DExtras
284 * \brief The QPlaneGeometry class allows creation of a plane in 3D space.
285 * \since 5.7
286 * \ingroup geometries
287 *
288 * The QPlaneGeometry class is most commonly used internally by the QPlaneMesh
289 * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses.
290 */
291
292/*!
293 * \fn Qt3DExtras::QPlaneGeometry::QPlaneGeometry(QNode *parent)
294 *
295 * Constructs a new QPlaneGeometry with \a parent.
296 */
297QPlaneGeometry::QPlaneGeometry(QPlaneGeometry::QNode *parent)
298 : QGeometry(*new QPlaneGeometryPrivate(), parent)
299{
300 Q_D(QPlaneGeometry);
301 d->init();
302}
303
304/*!
305 * \internal
306 */
307QPlaneGeometry::QPlaneGeometry(QPlaneGeometryPrivate &dd, QNode *parent)
308 : QGeometry(dd, parent)
309{
310 Q_D(QPlaneGeometry);
311 d->init();
312}
313
314/*!
315 * \internal
316 */
317QPlaneGeometry::~QPlaneGeometry()
318{
319}
320
321/*!
322 * Updates vertices based on mesh resolution, width, and height properties.
323 */
324void QPlaneGeometry::updateVertices()
325{
326 Q_D(QPlaneGeometry);
327 const int nVerts = d->m_meshResolution.width() * d->m_meshResolution.height();
328
329 d->m_positionAttribute->setCount(nVerts);
330 d->m_normalAttribute->setCount(nVerts);
331 d->m_texCoordAttribute->setCount(nVerts);
332 d->m_tangentAttribute->setCount(nVerts);
333 d->m_vertexBuffer->setDataGenerator(QSharedPointer<PlaneVertexBufferFunctor>::create(arguments&: d->m_width, arguments&: d->m_height, arguments&: d->m_meshResolution, arguments&: d->m_mirrored));
334}
335
336/*!
337 * Updates indices based on mesh resolution.
338 */
339void QPlaneGeometry::updateIndices()
340{
341 Q_D(QPlaneGeometry);
342 const int faces = 2 * (d->m_meshResolution.width() - 1) * (d->m_meshResolution.height() - 1);
343 // Each primitive has 3 vertices
344 d->m_indexAttribute->setCount(faces * 3);
345 d->m_indexBuffer->setDataGenerator(QSharedPointer<PlaneIndexBufferFunctor>::create(arguments&: d->m_meshResolution));
346
347}
348
349void QPlaneGeometry::setResolution(const QSize &resolution)
350{
351 Q_D(QPlaneGeometry);
352 if (d->m_meshResolution == resolution)
353 return;
354 d->m_meshResolution = resolution;
355 updateVertices();
356 updateIndices();
357 emit resolutionChanged(resolution);
358}
359
360void QPlaneGeometry::setWidth(float width)
361{
362 Q_D(QPlaneGeometry);
363 if (width == d->m_width)
364 return;
365 d->m_width = width;
366 updateVertices();
367 emit widthChanged(width);
368}
369
370void QPlaneGeometry::setHeight(float height)
371{
372 Q_D(QPlaneGeometry);
373 if (height == d->m_height)
374 return;
375 d->m_height = height;
376 updateVertices();
377 emit heightChanged(height);
378}
379
380void QPlaneGeometry::setMirrored(bool mirrored)
381{
382 Q_D(QPlaneGeometry);
383 if (mirrored == d->m_mirrored)
384 return;
385 d->m_mirrored = mirrored;
386 updateVertices();
387 emit mirroredChanged(mirrored);
388}
389
390/*!
391 * \property QPlaneGeometry::resolution
392 *
393 * Holds the plane resolution.
394 */
395QSize QPlaneGeometry::resolution() const
396{
397 Q_D(const QPlaneGeometry);
398 return d->m_meshResolution;
399}
400
401/*!
402 * \property QPlaneGeometry::width
403 *
404 * Holds the plane width.
405 */
406float QPlaneGeometry::width() const
407{
408 Q_D(const QPlaneGeometry);
409 return d->m_width;
410}
411
412/*!
413 * \property QPlaneGeometry::height
414 *
415 * Holds the plane height.
416 */
417float QPlaneGeometry::height() const
418{
419 Q_D(const QPlaneGeometry);
420 return d->m_height;
421}
422
423/*!
424 * \property QPlaneGeometry::mirrored
425 * \since 5.9
426 *
427 * Controls if the UV coordinates of the plane should be flipped vertically.
428 */
429bool QPlaneGeometry::mirrored() const
430{
431 Q_D(const QPlaneGeometry);
432 return d->m_mirrored;
433}
434
435/*!
436 * \property QPlaneGeometry::positionAttribute
437 *
438 * Holds the geometry position attribute.
439 */
440QAttribute *QPlaneGeometry::positionAttribute() const
441{
442 Q_D(const QPlaneGeometry);
443 return d->m_positionAttribute;
444}
445
446/*!
447 * \property QPlaneGeometry::normalAttribute
448 *
449 * Holds the geometry normal attribute.
450 */
451QAttribute *QPlaneGeometry::normalAttribute() const
452{
453 Q_D(const QPlaneGeometry);
454 return d->m_normalAttribute;
455}
456
457/*!
458 * \property QPlaneGeometry::texCoordAttribute
459 *
460 * Holds the geometry texture coordinate attribute.
461 */
462QAttribute *QPlaneGeometry::texCoordAttribute() const
463{
464 Q_D(const QPlaneGeometry);
465 return d->m_texCoordAttribute;
466}
467
468/*!
469 * \property QPlaneGeometry::tangentAttribute
470 *
471 * Holds the geometry tangent attribute.
472 */
473QAttribute *QPlaneGeometry::tangentAttribute() const
474{
475 Q_D(const QPlaneGeometry);
476 return d->m_tangentAttribute;
477}
478
479/*!
480 * \property QPlaneGeometry::indexAttribute
481 *
482 * Holds the geometry index attribute.
483 */
484QAttribute *QPlaneGeometry::indexAttribute() const
485{
486 Q_D(const QPlaneGeometry);
487 return d->m_indexAttribute;
488}
489
490QPlaneGeometryPrivate::QPlaneGeometryPrivate()
491 : QGeometryPrivate()
492 , m_width(1.0f)
493 , m_height(1.0f)
494 , m_meshResolution(QSize(2, 2))
495 , m_mirrored(false)
496 , m_positionAttribute(nullptr)
497 , m_normalAttribute(nullptr)
498 , m_texCoordAttribute(nullptr)
499 , m_tangentAttribute(nullptr)
500 , m_indexAttribute(nullptr)
501 , m_vertexBuffer(nullptr)
502 , m_indexBuffer(nullptr)
503{
504}
505
506void QPlaneGeometryPrivate::init()
507{
508 Q_Q(QPlaneGeometry);
509 m_positionAttribute = new QAttribute(q);
510 m_normalAttribute = new QAttribute(q);
511 m_texCoordAttribute = new QAttribute(q);
512 m_tangentAttribute = new QAttribute(q);
513 m_indexAttribute = new QAttribute(q);
514 m_vertexBuffer = new Qt3DRender::QBuffer(q);
515 m_indexBuffer = new Qt3DRender::QBuffer(q);
516
517 const int nVerts = m_meshResolution.width() * m_meshResolution.height();
518 const int stride = (3 + 2 + 3 + 4) * sizeof(float);
519 const int faces = 2 * (m_meshResolution.width() - 1) * (m_meshResolution.height() - 1);
520
521 m_positionAttribute->setName(QAttribute::defaultPositionAttributeName());
522 m_positionAttribute->setVertexBaseType(QAttribute::Float);
523 m_positionAttribute->setVertexSize(3);
524 m_positionAttribute->setAttributeType(QAttribute::VertexAttribute);
525 m_positionAttribute->setBuffer(m_vertexBuffer);
526 m_positionAttribute->setByteStride(stride);
527 m_positionAttribute->setCount(nVerts);
528
529 m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName());
530 m_texCoordAttribute->setVertexBaseType(QAttribute::Float);
531 m_texCoordAttribute->setVertexSize(2);
532 m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute);
533 m_texCoordAttribute->setBuffer(m_vertexBuffer);
534 m_texCoordAttribute->setByteStride(stride);
535 m_texCoordAttribute->setByteOffset(3 * sizeof(float));
536 m_texCoordAttribute->setCount(nVerts);
537
538 m_normalAttribute->setName(QAttribute::defaultNormalAttributeName());
539 m_normalAttribute->setVertexBaseType(QAttribute::Float);
540 m_normalAttribute->setVertexSize(3);
541 m_normalAttribute->setAttributeType(QAttribute::VertexAttribute);
542 m_normalAttribute->setBuffer(m_vertexBuffer);
543 m_normalAttribute->setByteStride(stride);
544 m_normalAttribute->setByteOffset(5 * sizeof(float));
545 m_normalAttribute->setCount(nVerts);
546
547 m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName());
548 m_tangentAttribute->setVertexBaseType(QAttribute::Float);
549 m_tangentAttribute->setVertexSize(4);
550 m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute);
551 m_tangentAttribute->setBuffer(m_vertexBuffer);
552 m_tangentAttribute->setByteStride(stride);
553 m_tangentAttribute->setByteOffset(8 * sizeof(float));
554 m_tangentAttribute->setCount(nVerts);
555
556 m_indexAttribute->setAttributeType(QAttribute::IndexAttribute);
557 m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort);
558 m_indexAttribute->setBuffer(m_indexBuffer);
559
560 // Each primitive has 3 vertives
561 m_indexAttribute->setCount(faces * 3);
562
563 m_vertexBuffer->setDataGenerator(QSharedPointer<PlaneVertexBufferFunctor>::create(arguments&: m_width, arguments&: m_height, arguments&: m_meshResolution, arguments&: m_mirrored));
564 m_indexBuffer->setDataGenerator(QSharedPointer<PlaneIndexBufferFunctor>::create(arguments&: m_meshResolution));
565
566 q->addAttribute(attribute: m_positionAttribute);
567 q->addAttribute(attribute: m_texCoordAttribute);
568 q->addAttribute(attribute: m_normalAttribute);
569 q->addAttribute(attribute: m_tangentAttribute);
570 q->addAttribute(attribute: m_indexAttribute);
571}
572
573} // Qt3DExtras
574
575QT_END_NAMESPACE
576

source code of qt3d/src/extras/geometries/qplanegeometry.cpp