1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qglrendersequencer.h"
#include "qglrenderorder.h"
#include "qglpainter.h"
#include "qglrenderordercomparator.h"
#include "qglrenderstate.h"
#include <QtCore/qstack.h>
QT_BEGIN_NAMESPACE
/*!
\class QGLRenderSequencer
\brief The QGLRenderSequencer class orders the rendering of QGLSceneNode instances.
\since 4.8
\ingroup qt3d
\ingroup qt3d::scene
The QGLRenderSequencer class works with the QGLRenderOrderComparator and
QGLRenderOrder classes to optimize the rendering order of scene nodes.
In general instances of this class are managed by QGLPainter and it should
not be necessary to explicitly create or manipulate them.
The render sequencer works by tracking instances of QGLRenderOrder objects
in a queue. As the scene graph is traversed during a call to a top-level
node's QGLSceneNode::draw() function, the sequencer adds one QGLRenderOrder
to the queue for each unique combination of rendering attributes.
The top level scene graph node loops once for each unique combination - it
does this in QGLSceneNode::draw() by calling nextInSequence(). At
each iteration, a current QGLRenderOrder is maintained, and only nodes
matching that order - as determined by \l{QGLRenderOrder::operator==()} -
are rendered in that pass. Non-matching nodes are added to a queue in the
order specified by \l{QGLRenderOrder::operator<()}.
Once an iteration/pass of
the scene graph is done, the next order is pulled from the front of the queue
and the current QGLRenderOrder is set to it.
Since the rendering attributes at a node are a function both of that node,
and attributes inherited from its parents, and since a given node may appear
multiple times at different places in the scene, it can thus have different
attributes and orders in each place. So there is no one-to-one mapping
between nodes and attributes.
To deal with this, QGLRenderOrder mappings are discovered during rendering.
There is no discovery pass. First, the initial QGLRenderOrder is lazily set
when the first geometry is actually drawn to the GPU - latching in that order
as the first current order. From that point, orders discovered that are
distinct from the current one are skipped in this rendering pass - by returning
false from renderInSequence() - and are instead added to the queue for rendering
on a subsequent pass.
When the final pass has been made, renderInSequence() returns false to the
top level QGLSceneNode, indicating that looping over passes is complete.
\sa QGLRenderOrder
*/
class QGLRenderSequencerPrivate
{
public:
QGLRenderSequencerPrivate(QGLPainter *painter);
~QGLRenderSequencerPrivate();
QGLSceneNode *top;
QLinkedList<QGLRenderOrder> queue;
QStack<QGLRenderState> stack;
QSet<QGLRenderOrder> exclude;
QGLRenderOrder current;
QGLPainter *painter;
QGLRenderOrderComparator *compare;
bool latched;
};
QGLRenderSequencerPrivate::QGLRenderSequencerPrivate(QGLPainter *painter)
: top(0)
, current(QGLRenderOrder())
, painter(painter)
, compare(new QGLRenderOrderComparator)
, latched(false)
{
}
QGLRenderSequencerPrivate::~QGLRenderSequencerPrivate()
{
delete compare;
}
/*!
Construct a new QGLRenderSequencer for the \a painter.
*/
QGLRenderSequencer::QGLRenderSequencer(QGLPainter *painter)
: d(new QGLRenderSequencerPrivate(painter))
{
}
/*!
Sets the render sequencer to operate on \a painter.
*/
void QGLRenderSequencer::setPainter(QGLPainter *painter)
{
d->painter = painter;
}
/*!
Returns the current top node of the rendering tree, or NULL if the
sequencer has been reset.
*/
QGLSceneNode *QGLRenderSequencer::top() const
{
return d->top;
}
/*!
Sets the current top node of the rendering tree to \a top.
*/
void QGLRenderSequencer::setTop(QGLSceneNode *top)
{
d->top = top;
}
/*!
Reset this sequencer to start from the top of the scene graph again.
After this call the top() function will return NULL, and any scene
node passed to the renderInSequence() function will be treated as the
top of the scene graph. Also effects and materials will be ignored
until latched in - QGLPainter::draw() will call latch() to achieve this.
\sa top()
*/
void QGLRenderSequencer::reset()
{
d->top = 0;
d->latched = false;
d->exclude.clear();
d->stack.clear();
d->current = QGLRenderOrder();
}
/*!
Returns true if there is a next rendering state in the queue; and if there
is a new order will be pulled from the queue, and its rendering attributes
- materials, effects and so on - will be applied to the painter for this
sequencer. Returns false when no more rendering states are queued and
scene is completely rendered.
*/
bool QGLRenderSequencer::nextInSequence()
{
bool nextAvailable = true;
if (d->queue.size() > 0)
{
// process thru next render order
d->current = d->queue.takeFirst();
}
else
{
// end top level loop
nextAvailable = false;
}
return nextAvailable;
}
/*!
Returns true, when this \a node should be rendered, in the order dictated
by QGLRenderOrder objects generated by the current render order Comparator.
The \a node must be non-NULL.
When this function returns false, indicating that rendering should be
skipped for the current pass, a check is made to ensure that a state object
for this nodes rendering attributes is queued up for a later pass.
To customize the ordering, reimplement QGLRenderOrder and
QGLRenderOrderComparator; then set the custom Comparator onto the current
painter using setcompare().
\sa reset(), top(), nextInSequence()
*/
bool QGLRenderSequencer::renderInSequence(QGLSceneNode *node)
{
Q_ASSERT(node);
Q_ASSERT(d->top);
bool doRender = true;
QGLRenderState state;
if (!d->stack.empty())
state = d->stack.top();
QGLRenderOrder o(node, state);
if (!d->current.isValid())
d->current = o;
if (d->latched && !d->compare->isEqualTo(o, d->current))
{
if (!d->exclude.contains(o))
insertNew(o);
doRender = false;
}
else
{
if (!d->latched)
d->exclude.insert(o);
}
return doRender;
}
/*!
Marks the render state for the \a node as started for this rendering pass. Call
this before rendering \a node, or any child nodes of \a node.
Once the rendering state is no longer valid, call endState().
To actually apply the effective state, as inherited from previous calls to
beginState() call the applyState() function.
\sa endState(), applyState()
*/
void QGLRenderSequencer::beginState(QGLSceneNode *node)
{
QGLRenderState state;
if (!d->stack.empty())
state = d->stack.top();
state.updateFrom(node);
d->stack.push(state);
}
/*!
Marks the render state for the \a node as done for this rendering pass.
If a node has called beginState(), then this function must be called to maintain
the rendering stack. If this function call is not matched by a previous
beginState() call undefined behaviour may result. In debug mode it may assert.
\sa beginState(), applyState()
*/
void QGLRenderSequencer::endState(QGLSceneNode *node)
{
#ifndef QT_NO_DEBUG_STREAM
const QGLSceneNode *n = d->stack.top().node();
Q_UNUSED(n);
Q_UNUSED(node);
Q_ASSERT(n == node);
#endif
d->stack.pop();
}
/*!
Applies the current rendering state to the painter for this sequencer.
\sa beginState(), endState()
*/
void QGLRenderSequencer::applyState()
{
d->latched = true;
QGLRenderState s = d->stack.top();
if (s.hasEffect() && !d->painter->isPicking())
{
if (s.userEffect())
{
if (d->painter->userEffect() != s.userEffect())
d->painter->setUserEffect(s.userEffect());
}
else
{
if (d->painter->userEffect() ||
d->painter->standardEffect() != s.standardEffect())
d->painter->setStandardEffect(s.standardEffect());
}
}
if (s.material() && !d->painter->isPicking())
{
QGLMaterial *mat = s.material();
if (1) //FIXME: d->painter->faceMaterial(QGL::FrontFaces) != mat)
{
d->painter->setFaceMaterial(QGL::FrontFaces, mat);
int texUnit = 0;
for (int i = 0; i < mat->textureLayerCount(); ++i)
{
QGLTexture2D *tex = mat->texture(i);
if (tex)
{
d->painter->glActiveTexture(GL_TEXTURE0 + texUnit);
tex->bind();
++texUnit;
}
}
}
}
}
void QGLRenderSequencer::insertNew(const QGLRenderOrder &order)
{
QLinkedList<QGLRenderOrder>::iterator it = d->queue.begin();
for ( ; it != d->queue.end(); ++it)
{
const QGLRenderOrder o = *it;
if (d->compare->isLessThan(order, o))
break;
}
d->queue.insert(it, order);
d->exclude.insert(order);
}
/*!
Returns the current render order comparator. By default this is an
instance of the base class QGLRenderOrderComparator.
\sa setComparator()
*/
QGLRenderOrderComparator *QGLRenderSequencer::comparator() const
{
return d->compare;
}
/*!
Sets the current render order \a comparator. This is only needed if a
custom rendering order is to be created. The argument \a comparator
must be non-null, or undefined behaviour will result.
Any existing current comparator is destroyed.
\sa renderInSequence(), comparator()
*/
void QGLRenderSequencer::setComparator(QGLRenderOrderComparator *comparator)
{
Q_ASSERT(comparator);
delete d->compare;
d->compare = comparator;
}
QT_END_NAMESPACE
|