Coverage Report

Created: 2025-07-18 07:08

/src/ogre/OgreMain/include/OgreRenderQueueSortingGrouping.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#ifndef __RenderQueueSortingGrouping_H__
29
#define __RenderQueueSortingGrouping_H__
30
31
// Precompiler options
32
#include "OgrePrerequisites.h"
33
#include "OgrePass.h"
34
#include "OgreHeaderPrefix.h"
35
36
namespace Ogre {
37
38
    /** \addtogroup Core
39
    *  @{
40
    */
41
    /** \addtogroup RenderSystem
42
    *  @{
43
    */
44
45
    typedef std::vector<Renderable*> RenderableList;
46
47
    /** Struct associating a single Pass with a single Renderable. 
48
        This is used to for objects sorted by depth and thus not
49
        grouped by pass.
50
    */
51
    struct RenderablePass
52
    {
53
        /// Pointer to the Renderable details
54
        Renderable* renderable;
55
        /// Pointer to the Pass
56
        Pass* pass;
57
58
0
        RenderablePass(Renderable* rend, Pass* p) :renderable(rend), pass(p) {}
59
    };
60
61
62
    /** Visitor interface for items in a QueuedRenderableCollection.
63
64
        Those wishing to iterate over the items in a 
65
        QueuedRenderableCollection should implement this visitor pattern,
66
        since internal organisation of the collection depends on the 
67
        sorting method in use.
68
    */
69
    class _OgreExport QueuedRenderableVisitor
70
    {
71
    public:
72
0
        QueuedRenderableVisitor() {}
73
0
        virtual ~QueuedRenderableVisitor() {}
74
        
75
        /** Called when visiting a RenderablePass, i.e. items in a
76
            sorted collection where items are not grouped by pass.
77
78
            If this is called, the other visit method
79
            will not be called.
80
        */
81
        virtual void visit(RenderablePass* rp) = 0;
82
83
        /** When visiting a collection grouped by pass, this is
84
            called.
85
86
            If this method is called, the RenderablePass visit 
87
            method will not be called for this collection.
88
        */
89
        virtual void visit(const Pass* p, RenderableList& rs) = 0;
90
    };
91
92
    /** Lowest level collection of renderables.
93
94
        To iterate over items in this collection, you must call
95
        the accept method and supply a QueuedRenderableVisitor.
96
        The order of the iteration, and whether that iteration is
97
        over a RenderablePass list or a 2-level grouped list which 
98
        causes a visit call at the Pass level, and a call for each
99
        Renderable underneath.
100
    */
101
    class _OgreExport QueuedRenderableCollection : public RenderQueueAlloc
102
    {
103
    public:
104
        /** Organisation modes required for this collection.
105
106
            This affects the internal placement of the items added to this collection;
107
            if only one type of sorting / grouping is to be required, then renderables
108
            can be stored only once, whilst if multiple types are going to be needed
109
            then internally there will be multiple organisations. Changing the organisation
110
            needs to be done when the collection is empty.
111
        */      
112
        enum OrganisationMode
113
        {
114
            /// Group by pass
115
            OM_PASS_GROUP = 1,
116
            /// Sort descending camera distance
117
            OM_SORT_DESCENDING = 2,
118
            /** Sort ascending camera distance 
119
                Note value overlaps with descending since both use same sort
120
            */
121
            OM_SORT_ASCENDING = 6
122
        };
123
124
    private:
125
        /// Comparator to order pass groups
126
        struct PassGroupLess
127
        {
128
            bool operator()(const Pass* a, const Pass* b) const
129
0
            {
130
0
                // Sort by passHash, which is pass, then texture unit changes
131
0
                uint32 hasha = a->getHash();
132
0
                uint32 hashb = b->getHash();
133
0
                if (hasha == hashb)
134
0
                {
135
0
                    // Must differentTransparentQueueItemLessiate by pointer in case 2 passes end up with the same hash
136
0
                    return a < b;
137
0
                }
138
0
                else
139
0
                {
140
0
                    return hasha < hashb;
141
0
                }
142
0
            }
143
        };
144
        /** Vector of RenderablePass objects, this is built on the assumption that
145
         vectors only ever increase in size, so even if we do clear() the memory stays
146
         allocated, ie fast */
147
        typedef std::vector<RenderablePass> RenderablePassList;
148
        /** Map of pass to renderable lists, this is a grouping by pass. */
149
        typedef std::map<Pass*, RenderableList, PassGroupLess> PassGroupRenderableMap;
150
151
        /// Bitmask of the organisation modes requested
152
        uint8 mOrganisationMode;
153
154
        /// Grouped 
155
        PassGroupRenderableMap mGrouped;
156
        /// Sorted descending (can iterate backwards to get ascending)
157
        RenderablePassList mSortedDescending;
158
159
        /// Internal visitor implementation
160
        void acceptVisitorGrouped(QueuedRenderableVisitor* visitor) const;
161
        /// Internal visitor implementation
162
        void acceptVisitorDescending(QueuedRenderableVisitor* visitor) const;
163
        /// Internal visitor implementation
164
        void acceptVisitorAscending(QueuedRenderableVisitor* visitor) const;
165
166
    public:
167
        QueuedRenderableCollection();
168
169
        /// Empty the collection
170
        void clear(void);
171
172
        /** Remove the group entry (if any) for a given Pass.
173
174
            To be used when a pass is destroyed, such that any
175
            grouping level for it becomes useless.
176
        */  
177
        void removePassGroup(Pass* p);
178
        
179
        /** Reset the organisation modes required for this collection. 
180
181
            You can only do this when the collection is empty.
182
        @see OrganisationMode
183
        */
184
        void resetOrganisationModes(void) 
185
0
        { 
186
0
            mOrganisationMode = 0; 
187
0
        }
188
        
189
        /** Add a required sorting / grouping mode to this collection when next used.
190
191
            You can only do this when the collection is empty.
192
        @see OrganisationMode
193
        */
194
        void addOrganisationMode(OrganisationMode om) 
195
0
        { 
196
0
            mOrganisationMode |= uint8(om);
197
0
        }
198
199
        /// Add a renderable to the collection using a given pass
200
        void addRenderable(Pass* pass, Renderable* rend);
201
        
202
        /** Perform any sorting that is required on this collection.
203
        @param cam The camera
204
        */
205
        void sort(const Camera* cam);
206
207
        /** Accept a visitor over the collection contents.
208
        @param visitor Visitor class which should be called back
209
        @param om The organisation mode which you want to iterate over.
210
            Note that this must have been included in an addOrganisationMode
211
            call before any renderables were added.
212
        */
213
        void acceptVisitor(QueuedRenderableVisitor* visitor, OrganisationMode om) const;
214
215
        /** Merge renderable collection. 
216
        */
217
        void merge( const QueuedRenderableCollection& rhs );
218
    };
219
220
    /** Collection of renderables by priority.
221
222
        This class simply groups renderables for rendering. All the 
223
        renderables contained in this class are destined for the same
224
        RenderQueueGroup (coarse groupings like those between the main
225
        scene and overlays) and have the same priority (fine groupings
226
        for detailed overlap control).
227
    @par
228
        This class can order solid renderables by a number of criteria; 
229
        it can optimise them into groups based on pass to reduce render 
230
        state changes, or can sort them by ascending or descending view 
231
        depth. Transparent objects are always ordered by descending depth.
232
    @par
233
        To iterate over items in the collections held by this object 
234
        you should retrieve the collection in use (e.g. solids, solids with
235
        no shadows, transparents) and use the accept() method, providing 
236
        a class implementing QueuedRenderableVisitor.
237
    
238
    */
239
    class _OgreExport RenderPriorityGroup : public RenderQueueAlloc
240
    {
241
    private:
242
243
        /// Parent queue group
244
        RenderQueueGroup* mParent;
245
        bool mSplitPassesByLightingType;
246
        bool mSplitNoShadowPasses;
247
        bool mShadowCastersNotReceivers;
248
        /// Solid pass list, used when no shadows, modulative shadows, or ambient passes for additive
249
        QueuedRenderableCollection mSolidsBasic;
250
        /// Solid per-light pass list, used with additive shadows
251
        QueuedRenderableCollection mSolidsDiffuseSpecular;
252
        /// Solid decal (texture) pass list, used with additive shadows
253
        QueuedRenderableCollection mSolidsDecal;
254
        /// Solid pass list, used when shadows are enabled but shadow receive is turned off for these passes
255
        QueuedRenderableCollection mSolidsNoShadowReceive;
256
        /// Unsorted transparent list
257
        QueuedRenderableCollection mTransparentsUnsorted;
258
        /// Transparent list
259
        QueuedRenderableCollection mTransparents;
260
261
        /// remove a pass entry from all collections
262
        void removePassEntry(Pass* p);
263
        /// Internal method for adding a solid renderable
264
        void addSolidRenderableSplitByLightType(Technique* pTech, Renderable* rend);
265
266
    public:
267
        RenderPriorityGroup(RenderQueueGroup* parent, 
268
            bool splitPassesByLightingType,
269
            bool splitNoShadowPasses, 
270
            bool shadowCastersNotReceivers); 
271
           
272
0
        ~RenderPriorityGroup() { }
273
274
        /** Get the collection of basic solids currently queued, this includes
275
            all solids when there are no shadows, or all solids which have shadow
276
            receiving enabled when using modulative shadows, or all ambient passes
277
            of solids which have shadow receive enabled for additive shadows. */
278
        const QueuedRenderableCollection& getSolidsBasic(void) const
279
0
        { return mSolidsBasic; }
280
        /** Get the collection of solids currently queued per light (only applicable in 
281
            additive shadow modes). */
282
        const QueuedRenderableCollection& getSolidsDiffuseSpecular(void) const
283
0
        { return mSolidsDiffuseSpecular; }
284
        /** Get the collection of solids currently queued for decal passes (only 
285
            applicable in additive shadow modes). */
286
        const QueuedRenderableCollection& getSolidsDecal(void) const
287
0
        { return mSolidsDecal; }
288
        /** Get the collection of solids for which shadow receipt is disabled (only
289
            applicable when shadows are enabled). */
290
        const QueuedRenderableCollection& getSolidsNoShadowReceive(void) const
291
0
        { return mSolidsNoShadowReceive; }
292
        /** Get the collection of transparent objects currently queued */
293
        const QueuedRenderableCollection& getTransparentsUnsorted(void) const
294
0
        { return mTransparentsUnsorted; }
295
        /** Get the collection of transparent objects currently queued */
296
        const QueuedRenderableCollection& getTransparents(void) const
297
0
        { return mTransparents; }
298
299
300
        /** Reset the organisation modes required for the solids in this group. 
301
302
            You can only do this when the group is empty, i.e. after clearing the 
303
            queue.
304
        @see QueuedRenderableCollection::OrganisationMode
305
        */
306
        void resetOrganisationModes(void);
307
        
308
        /** Add a required sorting / grouping mode for the solids in this group.
309
310
            You can only do this when the group is empty, i.e. after clearing the 
311
            queue.
312
        @see QueuedRenderableCollection::OrganisationMode
313
        */
314
        void addOrganisationMode(QueuedRenderableCollection::OrganisationMode om); 
315
316
        /** Set the sorting / grouping mode for the solids in this group to the default.
317
318
            You can only do this when the group is empty, i.e. after clearing the 
319
            queue.
320
        @see QueuedRenderableCollection::OrganisationMode
321
        */
322
        void defaultOrganisationMode(void); 
323
324
        /** Add a renderable to this group. */
325
        void addRenderable(Renderable* pRend, Technique* pTech);
326
327
        /** Sorts the objects which have been added to the queue; transparent objects by their 
328
            depth in relation to the passed in Camera. */
329
        void sort(const Camera* cam);
330
331
        /** Clears this group of renderables. 
332
        */
333
        void clear(void);
334
335
        /** Sets whether or not the queue will split passes by their lighting type,
336
        ie ambient, per-light and decal. 
337
        */
338
        void setSplitPassesByLightingType(bool split)
339
0
        {
340
0
            mSplitPassesByLightingType = split;
341
0
        }
342
343
        /** Sets whether or not passes which have shadow receive disabled should
344
            be separated. 
345
        */
346
        void setSplitNoShadowPasses(bool split)
347
0
        {
348
0
            mSplitNoShadowPasses = split;
349
0
        }
350
351
        /** Sets whether or not objects which cast shadows should be treated as
352
            never receiving shadows. 
353
        */
354
        void setShadowCastersCannotBeReceivers(bool ind)
355
0
        {
356
0
            mShadowCastersNotReceivers = ind;
357
0
        }
358
359
        /** Merge group of renderables. 
360
        */
361
        void merge( const RenderPriorityGroup* rhs );
362
363
364
    };
365
366
367
    /** A grouping level underneath RenderQueue which groups renderables
368
    to be issued at coarsely the same time to the renderer.
369
370
    Each instance of this class itself hold RenderPriorityGroup instances,
371
    which are the groupings of renderables by priority for fine control
372
    of ordering (not required for most instances).
373
    */
374
    class _OgreExport RenderQueueGroup : public RenderQueueAlloc
375
    {
376
    public:
377
        typedef std::map<ushort, RenderPriorityGroup*, std::less<ushort> > PriorityMap;
378
        typedef MapIterator<PriorityMap> PriorityMapIterator;
379
        typedef ConstMapIterator<PriorityMap> ConstPriorityMapIterator;
380
    private:
381
        bool mSplitPassesByLightingType;
382
        bool mSplitNoShadowPasses;
383
        bool mShadowCastersNotReceivers;
384
        /// Map of RenderPriorityGroup objects
385
        PriorityMap mPriorityGroups;
386
        /// Whether shadows are enabled for this queue
387
        bool mShadowsEnabled;
388
        /// Bitmask of the organisation modes requested (for new priority groups)
389
        uint8 mOrganisationMode;
390
391
392
    public:
393
        RenderQueueGroup(bool splitPassesByLightingType,
394
            bool splitNoShadowPasses,
395
            bool shadowCastersNotReceivers) 
396
            : mSplitPassesByLightingType(splitPassesByLightingType)
397
            , mSplitNoShadowPasses(splitNoShadowPasses)
398
            , mShadowCastersNotReceivers(shadowCastersNotReceivers)
399
            , mShadowsEnabled(true)
400
            , mOrganisationMode(0)
401
0
        {
402
0
        }
403
404
0
        ~RenderQueueGroup() {
405
0
            // destroy contents now
406
0
            PriorityMap::iterator i;
407
0
            for (i = mPriorityGroups.begin(); i != mPriorityGroups.end(); ++i)
408
0
            {
409
0
                OGRE_DELETE i->second;
410
0
            }
411
0
        }
412
413
0
        const PriorityMap& getPriorityGroups() const { return mPriorityGroups; }
414
415
        /** Add a renderable to this group, with the given priority. */
416
        void addRenderable(Renderable* pRend, Technique* pTech, ushort priority)
417
0
        {
418
0
            // Check if priority group is there
419
0
            PriorityMap::iterator i = mPriorityGroups.find(priority);
420
0
            RenderPriorityGroup* pPriorityGrp;
421
0
            if (i == mPriorityGroups.end())
422
0
            {
423
0
                // Missing, create
424
0
                pPriorityGrp = OGRE_NEW RenderPriorityGroup(this, 
425
0
                    mSplitPassesByLightingType,
426
0
                    mSplitNoShadowPasses, 
427
0
                    mShadowCastersNotReceivers);
428
0
                if (mOrganisationMode)
429
0
                {
430
0
                    pPriorityGrp->resetOrganisationModes();
431
0
                    pPriorityGrp->addOrganisationMode((QueuedRenderableCollection::OrganisationMode)mOrganisationMode);
432
0
                }
433
0
434
0
                mPriorityGroups.emplace(priority, pPriorityGrp);
435
0
            }
436
0
            else
437
0
            {
438
0
                pPriorityGrp = i->second;
439
0
            }
440
0
441
0
            // Add
442
0
            pPriorityGrp->addRenderable(pRend, pTech);
443
0
444
0
        }
445
446
        /** Clears this group of renderables. 
447
        @param destroy
448
            If false, doesn't delete any priority groups, just empties them. Saves on 
449
            memory deallocations since the chances are roughly the same kinds of 
450
            renderables are going to be sent to the queue again next time. If
451
            true, completely destroys.
452
        */
453
        void clear(bool destroy = false)
454
0
        {
455
0
            PriorityMap::iterator i, iend;
456
0
            iend = mPriorityGroups.end();
457
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
458
0
            {
459
0
                if (destroy)
460
0
                    OGRE_DELETE i->second;
461
0
                else
462
0
                    i->second->clear();
463
0
            }
464
0
465
0
            if (destroy)
466
0
                mPriorityGroups.clear();
467
0
468
0
        }
469
470
        /** Indicate whether a given queue group will be doing any
471
        shadow setup.
472
473
        This method allows you to inform the queue about a queue group, and to 
474
        indicate whether this group will require shadow processing of any sort.
475
        In order to preserve rendering order, OGRE has to treat queue groups
476
        as very separate elements of the scene, and this can result in it
477
        having to duplicate shadow setup for each group. Therefore, if you
478
        know that a group which you are using will never need shadows, you
479
        should preregister the group using this method in order to improve
480
        the performance.
481
        */
482
0
        void setShadowsEnabled(bool enabled) { mShadowsEnabled = enabled; }
483
484
        /** Are shadows enabled for this queue? */
485
0
        bool getShadowsEnabled(void) const { return mShadowsEnabled; }
486
487
        /** Sets whether or not the queue will split passes by their lighting type,
488
        ie ambient, per-light and decal. 
489
        */
490
        void setSplitPassesByLightingType(bool split)
491
0
        {
492
0
            mSplitPassesByLightingType = split;
493
0
            PriorityMap::iterator i, iend;
494
0
            iend = mPriorityGroups.end();
495
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
496
0
            {
497
0
                i->second->setSplitPassesByLightingType(split);
498
0
            }
499
0
        }
500
        /** Sets whether or not the queue will split passes which have shadow receive
501
        turned off (in their parent material), which is needed when certain shadow
502
        techniques are used.
503
        */
504
        void setSplitNoShadowPasses(bool split)
505
0
        {
506
0
            mSplitNoShadowPasses = split;
507
0
            PriorityMap::iterator i, iend;
508
0
            iend = mPriorityGroups.end();
509
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
510
0
            {
511
0
                i->second->setSplitNoShadowPasses(split);
512
0
            }
513
0
        }
514
        /** Sets whether or not objects which cast shadows should be treated as
515
        never receiving shadows. 
516
        */
517
        void setShadowCastersCannotBeReceivers(bool ind)
518
0
        {
519
0
            mShadowCastersNotReceivers = ind;
520
0
            PriorityMap::iterator i, iend;
521
0
            iend = mPriorityGroups.end();
522
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
523
0
            {
524
0
                i->second->setShadowCastersCannotBeReceivers(ind);
525
0
            }
526
0
        }
527
        /** Reset the organisation modes required for the solids in this group. 
528
529
            You can only do this when the group is empty, ie after clearing the 
530
            queue.
531
        @see QueuedRenderableCollection::OrganisationMode
532
        */
533
        void resetOrganisationModes(void)
534
0
        {
535
0
            mOrganisationMode = 0;
536
0
537
0
            PriorityMap::iterator i, iend;
538
0
            iend = mPriorityGroups.end();
539
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
540
0
            {
541
0
                i->second->resetOrganisationModes();
542
0
            }
543
0
        }
544
        
545
        /** Add a required sorting / grouping mode for the solids in this group.
546
547
            You can only do this when the group is empty, ie after clearing the 
548
            queue.
549
        @see QueuedRenderableCollection::OrganisationMode
550
        */
551
        void addOrganisationMode(QueuedRenderableCollection::OrganisationMode om)
552
0
        {
553
0
            mOrganisationMode |= uint8(om);
554
0
555
0
            PriorityMap::iterator i, iend;
556
0
            iend = mPriorityGroups.end();
557
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
558
0
            {
559
0
                i->second->addOrganisationMode(om);
560
0
            }
561
0
        }
562
563
        /** Setthe  sorting / grouping mode for the solids in this group to the default.
564
565
            You can only do this when the group is empty, ie after clearing the 
566
            queue.
567
        @see QueuedRenderableCollection::OrganisationMode
568
        */
569
        void defaultOrganisationMode(void)
570
0
        {
571
0
            mOrganisationMode = 0;
572
0
573
0
            PriorityMap::iterator i, iend;
574
0
            iend = mPriorityGroups.end();
575
0
            for (i = mPriorityGroups.begin(); i != iend; ++i)
576
0
            {
577
0
                i->second->defaultOrganisationMode();
578
0
            }
579
0
        }
580
581
        /** Merge group of renderables. 
582
        */
583
        void merge( const RenderQueueGroup* rhs )
584
0
        {
585
0
            for ( const auto& pg : rhs->getPriorityGroups() )
586
0
            {
587
0
                ushort priority = pg.first;
588
0
                RenderPriorityGroup* pSrcPriorityGrp = pg.second;
589
0
                RenderPriorityGroup* pDstPriorityGrp;
590
0
591
0
                // Check if priority group is there
592
0
                PriorityMap::iterator i = mPriorityGroups.find(priority);
593
0
                if (i == mPriorityGroups.end())
594
0
                {
595
0
                    // Missing, create
596
0
                    pDstPriorityGrp = OGRE_NEW RenderPriorityGroup(this, 
597
0
                        mSplitPassesByLightingType,
598
0
                        mSplitNoShadowPasses, 
599
0
                        mShadowCastersNotReceivers);
600
0
                    if (mOrganisationMode)
601
0
                    {
602
0
                        pDstPriorityGrp->resetOrganisationModes();
603
0
                        pDstPriorityGrp->addOrganisationMode((QueuedRenderableCollection::OrganisationMode)mOrganisationMode);
604
0
                    }
605
0
606
0
                    mPriorityGroups.emplace(priority, pDstPriorityGrp);
607
0
                }
608
0
                else
609
0
                {
610
0
                    pDstPriorityGrp = i->second;
611
0
                }
612
0
613
0
                // merge
614
0
                pDstPriorityGrp->merge( pSrcPriorityGrp );
615
0
            }
616
0
        }
617
    };
618
619
    /** @} */
620
    /** @} */
621
622
623
}
624
625
#include "OgreHeaderSuffix.h"
626
627
#endif
628
629