Coverage Report

Created: 2026-04-12 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreTextureShadowRenderer.cpp
Line
Count
Source
1
// This file is part of the OGRE project.
2
// It is subject to the license terms in the LICENSE file found in the top-level directory
3
// of this distribution and at https://www.ogre3d.org/licensing.
4
// SPDX-License-Identifier: MIT
5
6
#include "OgreStableHeaders.h"
7
#include "OgreShadowCameraSetup.h"
8
#include "OgreViewport.h"
9
#include "OgreHardwarePixelBuffer.h"
10
#include "OgreCompositorManager.h"
11
#include "OgreCompositor.h"
12
13
namespace Ogre {
14
15
SceneManager::TextureShadowRenderer::TextureShadowRenderer(SceneManager* owner) :
16
0
mSceneManager(owner),
17
0
mShadowCasterPlainBlackPass(0),
18
0
mShadowReceiverPass(0),
19
0
mShadowTextureCustomCasterPass(0),
20
0
mShadowTextureCustomReceiverPass(0),
21
0
mDefaultShadowFarDist(0),
22
0
mDefaultShadowFarDistSquared(0),
23
0
mShadowTextureOffset(0.6),
24
0
mShadowTextureFadeStart(0.7),
25
0
mShadowTextureFadeEnd(0.9),
26
0
mShadowTextureSelfShadow(false),
27
0
mShadowTextureConfigDirty(true),
28
0
mShadowCasterRenderBackFaces(true)
29
0
{
30
    // set up default shadow camera setup
31
0
    mDefaultShadowCameraSetup = DefaultShadowCameraSetup::create();
32
33
0
    mCullCameraSetup = DefaultShadowCameraSetup::create();
34
35
    // init shadow texture count per type.
36
0
    mShadowTextureCountPerType[Light::LT_POINT] = 1;
37
0
    mShadowTextureCountPerType[Light::LT_DIRECTIONAL] = 1;
38
0
    mShadowTextureCountPerType[Light::LT_SPOTLIGHT] = 1;
39
0
}
40
41
0
SceneManager::TextureShadowRenderer::~TextureShadowRenderer() {}
42
43
void SceneManager::TextureShadowRenderer::render(RenderQueueGroup* group,
44
                                          QueuedRenderableCollection::OrganisationMode om)
45
0
{
46
    // Receiver pass(es)
47
0
    if (mSceneManager->isShadowTechniqueAdditive())
48
0
    {
49
        // Auto-additive
50
0
        renderAdditiveTextureShadowedQueueGroupObjects(group, om);
51
0
        return;
52
0
    }
53
54
    // Modulative
55
0
    renderModulativeTextureShadowedQueueGroupObjects(group, om);
56
0
}
57
58
size_t SceneManager::TextureShadowRenderer::getShadowTexIndex(size_t startLightIndex)
59
0
{
60
0
    size_t shadowTexIndex = mShadowTextures.size();
61
0
    if (mShadowTextureIndexLightList.size() > startLightIndex)
62
0
        shadowTexIndex = mShadowTextureIndexLightList[startLightIndex];
63
0
    return shadowTexIndex;
64
0
}
65
//-----------------------------------------------------------------------
66
void SceneManager::TextureShadowRenderer::renderTextureShadowCasterQueueGroupObjects(
67
    RenderQueueGroup* pGroup,
68
    QueuedRenderableCollection::OrganisationMode om)
69
0
{
70
    // This is like the basic group render, except we skip all transparents
71
    // and we also render any non-shadowed objects
72
    // Note that non-shadow casters will have already been eliminated during
73
    // _findVisibleObjects
74
75
    // Iterate through priorities
76
77
    // Override auto param ambient to force vertex programs and fixed function to
78
0
    ColourValue currAmbient = mSceneManager->getAmbientLight();
79
0
    if (mSceneManager->isShadowTechniqueAdditive())
80
0
    {
81
        // Use simple black / white mask if additive
82
0
        mSceneManager->setAmbientLight(ColourValue::Black);
83
0
    }
84
0
    else
85
0
    {
86
        // Use shadow colour as caster colour if modulative
87
0
        mSceneManager->setAmbientLight(mSceneManager->getShadowColour());
88
0
    }
89
90
0
    auto visitor = mSceneManager->getQueuedRenderableVisitor();
91
0
    for (const auto& pg : pGroup->getPriorityGroups())
92
0
    {
93
0
        RenderPriorityGroup* pPriorityGrp = pg.second;
94
95
        // Sort the queue first
96
0
        pPriorityGrp->sort(mSceneManager->mCameraInProgress);
97
98
        // Do solids, override light list incase any vertex programs use them
99
0
        visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
100
0
        visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, false, false);
101
        // Do unsorted transparents that cast shadows
102
0
        visitor->renderObjects(pPriorityGrp->getTransparentsUnsorted(), om, false, false, nullptr, true);
103
        // Do transparents that cast shadows
104
0
        visitor->renderObjects(pPriorityGrp->getTransparents(), QueuedRenderableCollection::OM_SORT_DESCENDING, false,
105
0
                               false, nullptr, true);
106
107
0
    }// for each priority
108
109
    // reset ambient light
110
0
    mSceneManager->setAmbientLight(currAmbient);
111
0
}
112
//-----------------------------------------------------------------------
113
void SceneManager::TextureShadowRenderer::renderModulativeTextureShadowedQueueGroupObjects(
114
    RenderQueueGroup* pGroup,
115
    QueuedRenderableCollection::OrganisationMode om)
116
0
{
117
    /* For each light, we need to render all the solids from each group,
118
    then do the modulative shadows, then render the transparents from
119
    each group.
120
    Now, this means we are going to reorder things more, but that it required
121
    if the shadows are to look correct. The overall order is preserved anyway,
122
    it's just that all the transparents are at the end instead of them being
123
    interleaved as in the normal rendering loop.
124
    */
125
    // Iterate through priorities
126
0
    auto visitor = mSceneManager->getQueuedRenderableVisitor();
127
0
    for (const auto& pg : pGroup->getPriorityGroups())
128
0
    {
129
0
        RenderPriorityGroup* pPriorityGrp = pg.second;
130
131
        // Sort the queue first
132
0
        pPriorityGrp->sort(mSceneManager->mCameraInProgress);
133
134
        // Do solids
135
0
        visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true);
136
0
        visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true);
137
0
    }
138
139
140
    // Iterate over lights, render received shadows
141
    // only perform this if we're in the 'normal' render stage, to avoid
142
    // doing it during the render to texture
143
0
    if (mSceneManager->mIlluminationStage == IRS_NONE)
144
0
    {
145
0
        mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS;
146
147
0
        LightList::const_iterator i, iend;
148
0
        iend = mSceneManager->_getLightsAffectingFrustum().end();
149
150
0
        size_t si = 0;
151
0
        for (i = mSceneManager->_getLightsAffectingFrustum().begin();
152
0
             i != iend && si < mShadowTextures.size(); ++i)
153
0
        {
154
0
            Light* l = *i;
155
156
0
            if (!l->getCastShadows())
157
0
                continue;
158
159
            // Get camera for current shadow texture
160
0
            Camera *cam = mShadowTextures[si]->getBuffer()->getRenderTarget()->getViewport(0)->getCamera();
161
            // Hook up receiver texture
162
0
            Pass* targetPass = mShadowTextureCustomReceiverPass ?
163
0
                    mShadowTextureCustomReceiverPass : mShadowReceiverPass;
164
165
            // if this light is a spotlight, we need to add the spot fader layer
166
            // BUT not if using a custom projection matrix, since then it will be
167
            // inappropriately shaped most likely
168
0
            if (l->getType() == Light::LT_SPOTLIGHT && !cam->isCustomProjectionMatrixEnabled())
169
0
            {
170
                // remove all TUs except 0 & 1
171
                // (only an issue if additive shadows have been used)
172
0
                while(targetPass->getNumTextureUnitStates() > 2)
173
0
                    targetPass->removeTextureUnitState(2);
174
175
0
                TextureUnitState* t = NULL;
176
                // Add spot fader if not present already
177
0
                if (targetPass->getNumTextureUnitStates() == 2 &&
178
0
                    targetPass->getTextureUnitState(1)->_getTexturePtr() == mSpotFadeTexture)
179
0
                {
180
                    // Just set
181
0
                    t = targetPass->getTextureUnitState(1);
182
0
                }
183
0
                else
184
0
                {
185
                    // Remove any non-conforming spot layers
186
0
                    while(targetPass->getNumTextureUnitStates() > 1)
187
0
                        targetPass->removeTextureUnitState(1);
188
189
0
                    t = targetPass->createTextureUnitState();
190
0
                    t->setTexture(mSpotFadeTexture);
191
0
                    t->setColourOperation(LBO_ADD);
192
0
                    t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
193
0
                }
194
195
0
                t->setProjectiveTexturing(!targetPass->hasVertexProgram(), cam);
196
0
                mSceneManager->mAutoParamDataSource->setTextureProjector(cam, 1);
197
0
            }
198
0
            else
199
0
            {
200
                // remove all TUs except 0 including spot
201
0
                while(targetPass->getNumTextureUnitStates() > 1)
202
0
                    targetPass->removeTextureUnitState(1);
203
204
0
            }
205
206
            // account for the RTSS
207
0
            if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique())
208
0
            {
209
0
                targetPass = betterTechnique->getPass(0);
210
0
            }
211
212
0
            TextureUnitState* texUnit = targetPass->getTextureUnitState(0);
213
            // clamp to border colour in case this is a custom material
214
0
            texUnit->setSampler(mBorderSampler);
215
0
            resolveShadowTexture(texUnit, si, 0);
216
217
            // Set lighting / blending modes
218
0
            targetPass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO);
219
0
            targetPass->setLightingEnabled(false);
220
221
0
            targetPass->_load();
222
223
            // Fire pre-receiver event
224
0
            fireShadowTexturesPreReceiver(l, cam);
225
226
0
            renderTextureShadowReceiverQueueGroupObjects(pGroup, om);
227
228
0
            ++si;
229
230
0
        } // for each light
231
232
0
        mSceneManager->mIlluminationStage = IRS_NONE;
233
234
0
    }
235
0
}
236
//-----------------------------------------------------------------------
237
void SceneManager::TextureShadowRenderer::renderAdditiveTextureShadowedQueueGroupObjects(
238
    RenderQueueGroup* pGroup,
239
    QueuedRenderableCollection::OrganisationMode om)
240
0
{
241
0
    LightList lightList(1);
242
0
    auto visitor = mSceneManager->getQueuedRenderableVisitor();
243
244
0
    for (const auto& pg : pGroup->getPriorityGroups())
245
0
    {
246
0
        RenderPriorityGroup* pPriorityGrp = pg.second;
247
248
        // Sort the queue first
249
0
        pPriorityGrp->sort(mSceneManager->mCameraInProgress);
250
251
        // Render all the ambient passes first, no light iteration, no lights
252
0
        visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
253
        // Also render any objects which have receive shadows disabled
254
0
        visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true);
255
256
257
        // only perform this next part if we're in the 'normal' render stage, to avoid
258
        // doing it during the render to texture
259
0
        if (mSceneManager->mIlluminationStage == IRS_NONE)
260
0
        {
261
            // Iterate over lights, render masked
262
0
            size_t si = 0;
263
264
0
            for (Light* l : mSceneManager->_getLightsAffectingFrustum())
265
0
            {
266
0
                if (l->getCastShadows() && si < mShadowTextures.size())
267
0
                {
268
                    // Hook up receiver texture
269
0
                    Pass* targetPass = mShadowTextureCustomReceiverPass ?
270
0
                        mShadowTextureCustomReceiverPass : mShadowReceiverPass;
271
272
                    // account for the RTSS
273
0
                    if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique())
274
0
                    {
275
0
                        targetPass = betterTechnique->getPass(0);
276
0
                    }
277
278
0
                    TextureUnitState* texUnit = targetPass->getTextureUnitState(0);
279
                    // clamp to border colour in case this is a custom material
280
0
                    texUnit->setSampler(mBorderSampler);
281
0
                    resolveShadowTexture(texUnit, si, 0);
282
283
                    // Remove any spot fader layer
284
0
                    if (targetPass->getNumTextureUnitStates() > 1 &&
285
0
                        targetPass->getTextureUnitState(1)->getTextureName() == "spot_shadow_fade.dds")
286
0
                    {
287
                        // remove spot fader layer (should only be there if
288
                        // we previously used modulative shadows)
289
0
                        targetPass->removeTextureUnitState(1);
290
0
                    }
291
                    // Set lighting / blending modes
292
0
                    targetPass->setSceneBlending(SBF_ONE, SBF_ONE);
293
0
                    targetPass->setLightingEnabled(true);
294
0
                    targetPass->_load();
295
296
                    // increment shadow texture since used
297
0
                    ++si;
298
299
0
                    mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS;
300
301
0
                }
302
0
                else
303
0
                {
304
0
                    mSceneManager->mIlluminationStage = IRS_NONE;
305
0
                }
306
307
                // render lighting passes for this light
308
0
                lightList[0] = l;
309
310
                // set up light scissoring, always useful in additive modes
311
0
                ClipResult scissored = mSceneManager->buildAndSetScissor(lightList, mSceneManager->mCameraInProgress);
312
0
                ClipResult clipped = CLIPPED_NONE;
313
0
                if(mSceneManager->getShadowUseLightClipPlanes())
314
0
                    clipped = mSceneManager->buildAndSetLightClip(lightList);
315
                // skip if entirely clipped
316
0
                if(scissored == CLIPPED_ALL || clipped == CLIPPED_ALL)
317
0
                    continue;
318
319
0
                visitor->renderObjects(pPriorityGrp->getSolidsDiffuseSpecular(), om, false, false, &lightList);
320
0
                if (scissored == CLIPPED_SOME)
321
0
                    mSceneManager->resetScissor();
322
0
                if (clipped == CLIPPED_SOME)
323
0
                    mSceneManager->resetLightClip();
324
325
0
            }// for each light
326
327
0
            mSceneManager->mIlluminationStage = IRS_NONE;
328
329
            // Now render decal passes, no need to set lights as lighting will be disabled
330
0
            visitor->renderObjects(pPriorityGrp->getSolidsDecal(), om, false, false);
331
332
0
        }
333
334
335
0
    }// for each priority
336
0
}
337
//-----------------------------------------------------------------------
338
void SceneManager::TextureShadowRenderer::renderTextureShadowReceiverQueueGroupObjects(
339
    RenderQueueGroup* pGroup,
340
    QueuedRenderableCollection::OrganisationMode om)
341
0
{
342
    // Iterate through priorities
343
344
    // Override auto param ambient to force vertex programs to go full-bright
345
0
    ColourValue currAmbient = mSceneManager->getAmbientLight();
346
0
    mSceneManager->setAmbientLight(ColourValue::White);
347
0
    auto visitor = mSceneManager->getQueuedRenderableVisitor();
348
349
0
    for (const auto& pg : pGroup->getPriorityGroups())
350
0
    {
351
0
        RenderPriorityGroup* pPriorityGrp = pg.second;
352
353
        // Do solids, override light list incase any vertex programs use them
354
0
        visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false);
355
356
        // Don't render transparents or passes which have shadow receipt disabled
357
358
0
    }// for each priority
359
360
    // reset ambient
361
0
    mSceneManager->setAmbientLight(currAmbient);
362
0
}
363
//---------------------------------------------------------------------
364
void SceneManager::TextureShadowRenderer::setupRenderTarget(const String& camName, RenderTarget* rendTarget,
365
                                                            uint16 depthBufferId)
366
0
{
367
0
    if (rendTarget->getDepthBufferPool() != RBP_NONE)
368
0
    {
369
0
        rendTarget->setDepthBufferPool(depthBufferId);
370
0
    }
371
372
    // Don't update automatically - we'll do it when required
373
0
    rendTarget->setAutoUpdated(false);
374
375
0
    float aspectRatio = (Real)rendTarget->getWidth() / (Real)rendTarget->getHeight();
376
377
    // Create camera for this texture, but note that we have to rebind
378
    // in prepareShadowTextures to coexist with multiple SMs
379
0
    Camera* cam = mSceneManager->createCamera(camName);
380
0
    cam->setAspectRatio(aspectRatio);
381
0
    auto camNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
382
0
    camNode->attachObject(cam);
383
0
    mShadowTextureCameras.push_back(cam);
384
    // insert dummy camera-light combination
385
0
    mShadowCamLightMapping[cam] = NULL;
386
387
    // use separate culling camera, in case a focused shadow setup is used
388
    // in which case we want to keep the original light frustum for culling
389
0
    Camera* cullCam = mSceneManager->createCamera(camName+"/Cull");
390
0
    cullCam->setAspectRatio(aspectRatio);
391
0
    cam->setCullingFrustum(cullCam);
392
0
    camNode->attachObject(cullCam);
393
394
    // Create a viewport, if not there already
395
0
    if (rendTarget->getNumViewports() != 0)
396
0
        return;
397
398
    // Note camera assignment is transient when multiple SMs
399
0
    Viewport *v = rendTarget->addViewport(NULL);
400
0
    v->setClearEveryFrame(true);
401
0
    v->setOverlaysEnabled(false);
402
0
    v->setBackgroundColour(ColourValue::White);
403
0
}
404
405
void SceneManager::TextureShadowRenderer::ensureShadowTexturesCreated()
406
0
{
407
0
    if(!mBorderSampler)
408
0
    {
409
0
        mBorderSampler = TextureManager::getSingleton().createSampler();
410
0
        mBorderSampler->setAddressingMode(TAM_BORDER);
411
0
        mBorderSampler->setBorderColour(ColourValue::White);
412
0
        mBorderSampler->setFiltering(FT_MIP, FO_NONE); // we do not have mips. GLES2 is particularly picky here.
413
0
    }
414
415
0
    if (mShadowTextureConfigDirty)
416
0
    {
417
0
        destroyShadowTextures();
418
0
        ShadowTextureManager::getSingleton().getShadowTextures(mShadowTextureConfigList, mShadowTextures);
419
420
        // clear shadow cam - light mapping
421
0
        mShadowCamLightMapping.clear();
422
423
        //Used to get the depth buffer ID setting for each RTT
424
0
        size_t __i = 0;
425
426
        // Recreate shadow textures
427
0
        for (auto& shadowTex : mShadowTextures)
428
0
        {
429
0
            uint16 depthBufferId = mShadowTextureConfigList[__i].depthBufferPoolId;
430
0
            for(uint32 i = 0; i < shadowTex->getNumLayers(); i++)
431
0
            {
432
0
                String camName = StringUtil::format("%sCam%d", shadowTex->getName().c_str(), i);
433
0
                auto rtidx = shadowTex->getUsage() & TU_TARGET_ALL_LAYERS ? 0 : i;
434
0
                setupRenderTarget(camName, shadowTex->getRenderTarget(rtidx), depthBufferId);
435
0
            }
436
437
0
            ++__i;
438
0
        }
439
440
        // Assume first one is representative
441
0
        if (!mShadowTextureConfigList.empty())
442
0
            mNoShadowTexture =
443
0
                ShadowTextureManager::getSingleton().getNoShadowTexture(mShadowTextureConfigList[0].format);
444
445
0
        mShadowTextureConfigDirty = false;
446
0
    }
447
448
0
}
449
//---------------------------------------------------------------------
450
void SceneManager::TextureShadowRenderer::destroyShadowTextures(void)
451
0
{
452
0
    for (auto cam : mShadowTextureCameras)
453
0
    {
454
0
        mSceneManager->getRootSceneNode()->removeAndDestroyChild(cam->getParentSceneNode());
455
456
        // Always destroy camera since they are local to this SM
457
0
        if(auto cullcam = dynamic_cast<Camera*>(cam->getCullingFrustum()))
458
0
            mSceneManager->destroyCamera(cullcam);
459
460
0
        mSceneManager->destroyCamera(cam);
461
0
    }
462
0
    mShadowTextures.clear();
463
0
    mShadowTextureCameras.clear();
464
465
    // set by render*TextureShadowedQueueGroupObjects
466
0
    mSceneManager->mAutoParamDataSource->setTextureProjector(NULL, 0);
467
468
    // Will destroy if no other scene managers referencing
469
0
    ShadowTextureManager::getSingleton().clearUnused();
470
471
0
    mShadowTextureConfigDirty = true;
472
0
}
473
//---------------------------------------------------------------------
474
void SceneManager::TextureShadowRenderer::prepareTexCam(Camera* texCam, Camera* cam, Viewport* vp, Light* light,
475
                                                        size_t j)
476
0
{
477
    // Associate main view camera as LOD camera
478
0
    texCam->setLodCamera(cam);
479
    // set base
480
0
    if (light->getType() != Light::LT_POINT)
481
0
    {
482
#ifdef OGRE_NODELESS_POSITIONING
483
        texCam->getParentSceneNode()->setDirection(light->getDerivedDirection(), Node::TS_WORLD);
484
#else
485
0
        texCam->getParentSceneNode()->setOrientation(light->getParentNode()->_getDerivedOrientation());
486
0
#endif
487
0
    }
488
0
    if (light->getType() != Light::LT_DIRECTIONAL)
489
0
        texCam->getParentSceneNode()->setPosition(light->getDerivedPosition());
490
491
    // update shadow cam - light mapping
492
0
    ShadowCamLightMapping::iterator camLightIt = mShadowCamLightMapping.find(texCam);
493
0
    assert(camLightIt != mShadowCamLightMapping.end());
494
0
    camLightIt->second = light;
495
496
0
    if (!light->getCustomShadowCameraSetup())
497
0
        mDefaultShadowCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, texCam, j);
498
0
    else
499
0
        light->getCustomShadowCameraSetup()->getShadowCamera(mSceneManager, cam, vp, light, texCam, j);
500
501
    // Fire shadow caster update, callee can alter camera settings
502
0
    fireShadowTexturesPreCaster(light, texCam, j);
503
0
}
504
void SceneManager::TextureShadowRenderer::updateShadowTextures(Camera* cam, Viewport* vp, const LightList* lightList)
505
0
{
506
    // Determine far shadow distance
507
0
    Real shadowDist = mDefaultShadowFarDist;
508
0
    if (!shadowDist)
509
0
    {
510
        // need a shadow distance, make one up
511
0
        shadowDist = cam->getNearClipDistance() * 300;
512
0
    }
513
0
    Real shadowOffset = shadowDist * mShadowTextureOffset;
514
    // Precalculate fading info
515
0
    Real shadowEnd = shadowDist + shadowOffset;
516
0
    Real fadeStart = shadowEnd * mShadowTextureFadeStart;
517
0
    Real fadeEnd = shadowEnd * mShadowTextureFadeEnd;
518
    // Additive lighting should not use fogging, since it will overbrighten; use border clamp
519
0
    if (!mSceneManager->isShadowTechniqueAdditive())
520
0
    {
521
        // set fogging to hide the shadow edge
522
0
        mShadowReceiverPass->setFog(true, FOG_LINEAR, ColourValue::White, 0, fadeStart, fadeEnd);
523
0
    }
524
0
    else
525
0
    {
526
        // disable fogging explicitly
527
0
        mShadowReceiverPass->setFog(true, FOG_NONE);
528
0
    }
529
530
    // Iterate over the lights we've found, max out at the limit of light textures
531
    // Note that the light sorting must now place shadow casting lights at the
532
    // start of the light list, therefore we do not need to deal with potential
533
    // mismatches in the light<->shadow texture list any more
534
535
0
    LightList::const_iterator i, iend;
536
0
    ShadowTextureList::iterator si, siend;
537
0
    CameraList::iterator ci;
538
0
    iend = lightList->end();
539
0
    siend = mShadowTextures.end();
540
0
    ci = mShadowTextureCameras.begin();
541
0
    mShadowTextureIndexLightList.clear();
542
0
    size_t shadowTextureIndex = 0;
543
544
0
    for (i = lightList->begin(), si = mShadowTextures.begin(); i != iend && si != siend; ++i)
545
0
    {
546
0
        Light* light = *i;
547
548
        // skip light if shadows are disabled
549
0
        if (!light->getCastShadows())
550
0
            continue;
551
552
0
        mDestRenderSystem->_setDepthClamp(light->getType() == Light::LT_DIRECTIONAL);
553
554
        // texture iteration per light.
555
0
        size_t textureCountPerLight = mShadowTextureCountPerType[light->getType()];
556
0
        for (size_t j = 0; j < textureCountPerLight && si != siend; ++j)
557
0
        {
558
0
            TexturePtr &shadowTex = *si;
559
560
0
            size_t layer = j < shadowTex->getDepth() ? j : 0;
561
0
            size_t face = j < shadowTex->getNumFaces() ? j : 0;
562
0
            RenderTarget *shadowRTT = shadowTex->getBuffer(face)->getRenderTarget(layer);
563
0
            Viewport *shadowView = shadowRTT->getViewport(0);
564
0
            Camera *texCam = *ci;
565
            // rebind camera, incase another SM in use which has switched to its cam
566
0
            shadowView->setCamera(texCam);
567
568
            // also update culling camera
569
0
            auto cullCam = dynamic_cast<Camera*>(texCam->getCullingFrustum());
570
0
            cullCam->_notifyViewport(shadowView);
571
0
            mCullCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, cullCam, j);
572
573
            // Use the material scheme of the main viewport
574
            // This is required to pick up the correct shadow_caster_material and similar properties.
575
0
            shadowView->setMaterialScheme(vp->getMaterialScheme());
576
577
            // Set the viewport visibility flags
578
0
            shadowView->setVisibilityMask(light->getLightMask() & vp->getVisibilityMask());
579
580
0
            bool allLayers = shadowTex->getUsage() & TU_TARGET_ALL_LAYERS;
581
0
            size_t layers = std::max(shadowTex->getDepth(), shadowTex->getNumFaces());
582
583
0
            prepareTexCam(texCam, cam, vp, light, j);
584
0
            if(allLayers && layers <= textureCountPerLight)
585
0
            {
586
0
                std::vector<const Camera*> cams = {texCam};
587
0
                j++;
588
0
                for(; j < layers; ++j)
589
0
                {
590
0
                    texCam = *(++ci); // next camera
591
0
                    texCam->_notifyViewport(shadowView);
592
0
                    prepareTexCam(texCam, cam, vp, light, j);
593
0
                    cams.push_back(texCam);
594
0
                }
595
0
                mSceneManager->setVPRTCameras(cams);
596
0
            }
597
598
            // Update target
599
0
            shadowRTT->update();
600
601
0
            if((j + 1) >= layers)
602
0
                ++si; // next shadow texture
603
0
            ++ci; // next camera
604
0
        }
605
606
0
        mDestRenderSystem->_setDepthClamp(false);
607
608
        // set the first shadow texture index for this light.
609
0
        mShadowTextureIndexLightList.push_back(shadowTextureIndex);
610
0
        shadowTextureIndex += textureCountPerLight;
611
0
    }
612
613
0
    fireShadowTexturesUpdated(std::min(lightList->size(), mShadowTextures.size()));
614
615
0
    ShadowTextureManager::getSingleton().clearUnused();
616
0
}
617
618
619
void SceneManager::TextureShadowRenderer::setShadowTextureCasterMaterial(const MaterialPtr& mat)
620
0
{
621
0
    if(!mat) {
622
0
        mShadowTextureCustomCasterPass = 0;
623
0
        return;
624
0
    }
625
626
0
    mat->load();
627
0
    if (!mat->getBestTechnique())
628
0
    {
629
        // unsupported
630
0
        mShadowTextureCustomCasterPass = 0;
631
0
    }
632
0
    else
633
0
    {
634
0
        OgreAssert(!mat->getTechnique(0)->getPasses().empty(), "technique 0 has no passes");
635
0
        mShadowTextureCustomCasterPass = mat->getTechnique(0)->getPass(0);
636
0
    }
637
0
}
638
//---------------------------------------------------------------------
639
void SceneManager::TextureShadowRenderer::setShadowTextureReceiverMaterial(const MaterialPtr& mat)
640
0
{
641
0
    if(!mat) {
642
0
        mShadowTextureCustomReceiverPass = 0;
643
0
        return;
644
0
    }
645
646
0
    mat->load();
647
0
    if (!mat->getBestTechnique())
648
0
    {
649
        // unsupported
650
0
        mShadowTextureCustomReceiverPass = 0;
651
0
    }
652
0
    else
653
0
    {
654
0
        OgreAssert(!mat->getTechnique(0)->getPasses().empty(), "technique 0 has no passes");
655
0
        mShadowTextureCustomReceiverPass = mat->getTechnique(0)->getPass(0);
656
0
    }
657
0
}
658
//---------------------------------------------------------------------
659
void SceneManager::TextureShadowRenderer::setShadowTechnique(ShadowTechnique technique)
660
0
{
661
0
    if (mSceneManager->getShadowTechnique() == SHADOWTYPE_TEXTURE_MODULATIVE && !mSpotFadeTexture)
662
0
        mSpotFadeTexture = TextureManager::getSingleton().load("spot_shadow_fade.dds", RGN_INTERNAL);
663
664
0
    if (!mSceneManager->isShadowTechniqueTextureBased())
665
0
    {
666
        // Destroy shadow textures to optimise resource usage
667
0
        destroyShadowTextures();
668
0
        mSpotFadeTexture.reset();
669
0
    }
670
0
    else
671
0
    {
672
        // assure no custom shadow matrix is used accidentally in case we switch
673
        // from a custom shadow mapping type to a non-custom (uniform shadow mapping)
674
0
        for ( Camera* texCam : mShadowTextureCameras)
675
0
        {
676
0
            texCam->setCustomViewMatrix(false);
677
0
            texCam->setCustomProjectionMatrix(false);
678
0
        }
679
0
    }
680
681
0
    if(!mSceneManager->getShadowTechnique())
682
0
        return;
683
684
    // init shadow caster material for texture shadows
685
0
    if (!mShadowCasterPlainBlackPass)
686
0
    {
687
0
        MaterialPtr matPlainBlack = MaterialManager::getSingleton().getByName("Ogre/TextureShadowCaster");
688
0
        matPlainBlack->load();
689
0
        mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0);
690
0
    }
691
692
0
    if (!mShadowReceiverPass)
693
0
    {
694
0
        MaterialPtr matShadRec = MaterialManager::getSingleton().getByName("Ogre/TextureShadowReceiver", RGN_INTERNAL);
695
0
        if (!matShadRec)
696
0
        {
697
0
            matShadRec = MaterialManager::getSingleton().create("Ogre/TextureShadowReceiver", RGN_INTERNAL);
698
0
            mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0);
699
            // Don't set lighting and blending modes here, depends on additive / modulative
700
0
            TextureUnitState* t = mShadowReceiverPass->createTextureUnitState();
701
0
            t->setProjectiveTexturing(true, NULL); // will be set later, but the RTSS needs to know about this
702
0
        }
703
0
        else
704
0
        {
705
0
            mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0);
706
0
        }
707
0
    }
708
0
}
709
710
711
const Pass* SceneManager::TextureShadowRenderer::deriveShadowCasterPass(const Pass* pass)
712
0
{
713
0
    Pass* retPass;
714
0
    if (pass->getParent()->getShadowCasterMaterial())
715
0
    {
716
0
        auto bestTech = pass->getParent()->getShadowCasterMaterial()->getBestTechnique();
717
0
        if (bestTech && bestTech->getNumPasses() > 0) {
718
0
            return bestTech->getPass(0);
719
0
        }
720
0
    }
721
0
    retPass = mShadowTextureCustomCasterPass ?
722
0
        mShadowTextureCustomCasterPass : mShadowCasterPlainBlackPass;
723
724
    // Special case alpha-blended passes
725
0
    if ((pass->getSourceBlendFactor() == SBF_SOURCE_ALPHA &&
726
0
        pass->getDestBlendFactor() == SBF_ONE_MINUS_SOURCE_ALPHA)
727
0
        || pass->getAlphaRejectFunction() != CMPF_ALWAYS_PASS)
728
0
    {
729
        // Alpha blended passes must retain their transparency
730
0
        retPass->setAlphaRejectSettings(pass->getAlphaRejectFunction(),
731
0
            pass->getAlphaRejectValue());
732
0
        retPass->setSceneBlending(pass->getSourceBlendFactor(), pass->getDestBlendFactor());
733
0
        retPass->getParent()->getParent()->setTransparencyCastsShadows(true);
734
735
        // So we allow the texture units, but override the colour functions
736
        // Copy texture state, shift up one since 0 is shadow texture
737
0
        unsigned short origPassTUCount = pass->getNumTextureUnitStates();
738
0
        for (unsigned short t = 0; t < origPassTUCount; ++t)
739
0
        {
740
0
            TextureUnitState* tex;
741
0
            if (retPass->getNumTextureUnitStates() <= t)
742
0
            {
743
0
                tex = retPass->createTextureUnitState();
744
0
            }
745
0
            else
746
0
            {
747
0
                tex = retPass->getTextureUnitState(t);
748
0
            }
749
            // copy base state
750
0
            (*tex) = *(pass->getTextureUnitState(t));
751
            // override colour function
752
0
            tex->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT,
753
0
                                      mSceneManager->isShadowTechniqueAdditive() ? ColourValue::Black : mSceneManager->getShadowColour());
754
0
        }
755
        // Remove any extras
756
0
        while (retPass->getNumTextureUnitStates() > origPassTUCount)
757
0
        {
758
0
            retPass->removeTextureUnitState(origPassTUCount);
759
0
        }
760
761
0
    }
762
0
    else
763
0
    {
764
        // reset
765
0
        retPass->setSceneBlending(SBT_REPLACE);
766
0
        retPass->setAlphaRejectFunction(CMPF_ALWAYS_PASS);
767
0
        while (retPass->getNumTextureUnitStates() > 0)
768
0
        {
769
0
            retPass->removeTextureUnitState(0);
770
0
        }
771
0
    }
772
773
    // give the RTSS a chance to generate a better technique
774
0
    retPass->getParent()->getParent()->load();
775
776
0
    Technique* btech = retPass->getParent()->getParent()->getBestTechnique();
777
0
    if( btech )
778
0
    {
779
0
        retPass = btech->getPass(0);
780
0
    }
781
782
    // Propagate culling modes
783
0
    retPass->setCullingMode(pass->getCullingMode());
784
0
    retPass->setManualCullingMode(pass->getManualCullingMode());
785
786
0
    return retPass;
787
0
}
788
//---------------------------------------------------------------------
789
const Pass* SceneManager::TextureShadowRenderer::deriveShadowReceiverPass(const Pass* pass)
790
0
{
791
0
    if (pass->getParent()->getShadowReceiverMaterial())
792
0
    {
793
0
        return pass->getParent()->getShadowReceiverMaterial()->getBestTechnique()->getPass(0);
794
0
    }
795
796
0
    Pass* retPass = mShadowTextureCustomReceiverPass ? mShadowTextureCustomReceiverPass : mShadowReceiverPass;
797
798
0
    unsigned short keepTUCount;
799
    // If additive, need lighting parameters & standard programs
800
0
    if (mSceneManager->isShadowTechniqueAdditive())
801
0
    {
802
0
        retPass->setLightingEnabled(true);
803
0
        retPass->setAmbient(pass->getAmbient());
804
0
        retPass->setSelfIllumination(pass->getSelfIllumination());
805
0
        retPass->setDiffuse(pass->getDiffuse());
806
0
        retPass->setSpecular(pass->getSpecular());
807
0
        retPass->setShininess(pass->getShininess());
808
0
        retPass->setLightMask(pass->getLightMask());
809
810
        // We need to keep alpha rejection settings
811
0
        retPass->setAlphaRejectSettings(pass->getAlphaRejectFunction(),
812
0
            pass->getAlphaRejectValue());
813
        // Copy texture state, shift up one since 0 is shadow texture
814
0
        unsigned short origPassTUCount = pass->getNumTextureUnitStates();
815
0
        for (unsigned short t = 0; t < origPassTUCount; ++t)
816
0
        {
817
0
            unsigned short targetIndex = t+1;
818
0
            TextureUnitState* tex;
819
0
            if (retPass->getNumTextureUnitStates() <= targetIndex)
820
0
            {
821
0
                tex = retPass->createTextureUnitState();
822
0
            }
823
0
            else
824
0
            {
825
0
                tex = retPass->getTextureUnitState(targetIndex);
826
0
            }
827
0
            (*tex) = *(pass->getTextureUnitState(t));
828
            // If programmable, have to adjust the texcoord sets too
829
            // D3D insists that texcoordsets match tex unit in programmable mode
830
0
            if (retPass->hasVertexProgram())
831
0
                tex->setTextureCoordSet(targetIndex);
832
0
        }
833
0
        keepTUCount = origPassTUCount + 1;
834
0
    }// additive lighting
835
0
    else
836
0
    {
837
        // need to keep spotlight fade etc
838
0
        keepTUCount = retPass->getNumTextureUnitStates();
839
0
    }
840
841
    // re-/set light iteration settings
842
0
    retPass->setIteratePerLight(pass->getIteratePerLight(), pass->getRunOnlyForOneLightType(),
843
0
                                pass->getOnlyLightType());
844
845
    // Remove any extra texture units
846
0
    while (retPass->getNumTextureUnitStates() > keepTUCount)
847
0
    {
848
0
        retPass->removeTextureUnitState(keepTUCount);
849
0
    }
850
851
    // give the RTSS a chance to generate a better technique
852
0
    retPass->getParent()->getParent()->load();
853
854
0
    Technique* btech = retPass->getParent()->getParent()->getBestTechnique();
855
0
    if( btech )
856
0
    {
857
0
        retPass = btech->getPass(0);
858
0
    }
859
860
0
    return retPass;
861
0
}
862
863
const Pass* SceneManager::TextureShadowRenderer::deriveTextureShadowPass(const Pass* pass)
864
0
{
865
0
    if (mSceneManager->_getCurrentRenderStage() == IRS_RENDER_TO_TEXTURE)
866
0
    {
867
        // Derive a special shadow caster pass from this one
868
0
        return deriveShadowCasterPass(pass);
869
0
    }
870
871
0
    if (mSceneManager->_getCurrentRenderStage() == IRS_RENDER_RECEIVER_PASS)
872
0
    {
873
0
        return deriveShadowReceiverPass(pass);
874
0
    }
875
876
0
    return pass;
877
0
}
878
879
//---------------------------------------------------------------------
880
const VisibleObjectsBoundsInfo&
881
SceneManager::TextureShadowRenderer::getShadowCasterBoundsInfo( const Light* light, size_t iteration ) const
882
0
{
883
0
    static VisibleObjectsBoundsInfo nullBox;
884
885
    // find light
886
0
    unsigned int foundCount = 0;
887
0
    for (auto& m : mShadowCamLightMapping)
888
0
    {
889
0
        if (m.second == light )
890
0
        {
891
0
            if (foundCount == iteration)
892
0
            {
893
                // search the camera-aab list for the texture cam
894
0
                auto camIt = mSceneManager->mCamVisibleObjectsMap.find(m.first);
895
896
0
                if (camIt == mSceneManager->mCamVisibleObjectsMap.end())
897
0
                {
898
0
                    return nullBox;
899
0
                }
900
0
                else
901
0
                {
902
0
                    return camIt->second;
903
0
                }
904
0
            }
905
0
            else
906
0
            {
907
                // multiple shadow textures per light, keep searching
908
0
                ++foundCount;
909
0
            }
910
0
        }
911
0
    }
912
913
    // AAB not available
914
0
    return nullBox;
915
0
}
916
917
918
//---------------------------------------------------------------------
919
void SceneManager::TextureShadowRenderer::setShadowTextureConfig(size_t shadowIndex, unsigned short width,
920
    unsigned short height, PixelFormat format, unsigned short fsaa, uint16 depthBufferPoolId )
921
0
{
922
0
    ShadowTextureConfig conf;
923
0
    conf.width = width;
924
0
    conf.height = height;
925
0
    conf.format = format;
926
0
    conf.fsaa = fsaa;
927
0
    conf.depthBufferPoolId = depthBufferPoolId;
928
929
0
    setShadowTextureConfig(shadowIndex, conf);
930
931
932
0
}
933
//---------------------------------------------------------------------
934
void SceneManager::TextureShadowRenderer::setShadowTextureConfig(size_t shadowIndex,
935
    const ShadowTextureConfig& config)
936
0
{
937
0
    if (shadowIndex >= mShadowTextureConfigList.size())
938
0
    {
939
0
        OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
940
0
            "shadowIndex out of bounds",
941
0
            "SceneManager::setShadowTextureConfig");
942
0
    }
943
0
    mShadowTextureConfigList[shadowIndex] = config;
944
945
0
    mShadowTextureConfigDirty = true;
946
0
}
947
948
void SceneManager::TextureShadowRenderer::setShadowTextureCompositor(const String& compositorName,
949
                                                                     const String& resourceGroup)
950
0
{
951
0
    auto compositor = CompositorManager::getSingleton().getByName(compositorName, resourceGroup);
952
0
    OgreAssert(compositor, "Compositor not found");
953
0
    OgreAssert(compositor->getNumTechniques() > 0, "Compositor has no techniques");
954
955
0
    auto compositorTech = compositor->getTechnique(0);
956
0
    const auto& textures = compositorTech->getTextureDefinitions();
957
0
    OgreAssert(!textures.empty(), "Compositor technique has no textures");
958
959
0
    mShadowTextureConfigList.clear();
960
0
    mShadowTextureConfigDirty = true;
961
962
0
    for(const auto& texture : textures)
963
0
    {
964
0
        OgreAssert(texture->width && texture->height, "Compositor texture definition must have absolute size");
965
0
        OgreAssert(texture->formatList.size() == 1, "Compositor texture definition must have exactly one format");
966
967
0
        ShadowTextureConfig cfg;
968
0
        cfg.width = texture->width;
969
0
        cfg.height = texture->height;
970
0
        cfg.format = texture->formatList.front();
971
0
        cfg.fsaa = texture->fsaa > 1 ? texture->fsaa : 0;
972
0
        cfg.depthBufferPoolId = texture->depthBufferId;
973
0
        cfg.type = texture->type;
974
0
        cfg.depth = texture->depth;
975
976
0
        mShadowTextureConfigList.push_back(cfg);
977
0
    }
978
0
}
979
980
//---------------------------------------------------------------------
981
void SceneManager::TextureShadowRenderer::setShadowTextureSize(unsigned short size)
982
0
{
983
    // default all current
984
0
    for (auto & i : mShadowTextureConfigList)
985
0
    {
986
0
        if (i.width != size || i.height != size)
987
0
        {
988
0
            i.width = i.height = size;
989
0
            mShadowTextureConfigDirty = true;
990
0
        }
991
0
    }
992
993
0
}
994
//---------------------------------------------------------------------
995
void SceneManager::TextureShadowRenderer::setShadowTextureCount(size_t count)
996
0
{
997
    // Change size, any new items will need defaults
998
0
    if (count != mShadowTextureConfigList.size())
999
0
    {
1000
        // if no entries yet, use the defaults
1001
0
        if (mShadowTextureConfigList.empty())
1002
0
        {
1003
0
            mShadowTextureConfigList.resize(count);
1004
0
        }
1005
0
        else
1006
0
        {
1007
            // create new instances with the same settings as the last item in the list
1008
0
            mShadowTextureConfigList.resize(count, *mShadowTextureConfigList.rbegin());
1009
0
        }
1010
0
        mShadowTextureConfigDirty = true;
1011
0
    }
1012
0
}
1013
//---------------------------------------------------------------------
1014
void SceneManager::TextureShadowRenderer::setShadowTexturePixelFormat(PixelFormat fmt)
1015
0
{
1016
0
    for (auto & i : mShadowTextureConfigList)
1017
0
    {
1018
0
        if (i.format != fmt)
1019
0
        {
1020
0
            i.format = fmt;
1021
0
            mShadowTextureConfigDirty = true;
1022
0
        }
1023
0
    }
1024
0
}
1025
void SceneManager::TextureShadowRenderer::setShadowTextureFSAA(unsigned short fsaa)
1026
0
{
1027
0
    for (auto & i : mShadowTextureConfigList)
1028
0
    {
1029
0
        if (i.fsaa != fsaa)
1030
0
        {
1031
0
            i.fsaa = fsaa;
1032
0
            mShadowTextureConfigDirty = true;
1033
0
        }
1034
0
    }
1035
0
}
1036
//---------------------------------------------------------------------
1037
void SceneManager::TextureShadowRenderer::setShadowTextureSettings(unsigned short size,
1038
    unsigned short count, PixelFormat fmt, unsigned short fsaa, uint16 depthBufferPoolId)
1039
0
{
1040
0
    setShadowTextureCount(count);
1041
0
    for (auto & i : mShadowTextureConfigList)
1042
0
    {
1043
0
        if (i.width != size || i.height != size || i.format != fmt || i.fsaa != fsaa)
1044
0
        {
1045
0
            i.width = i.height = size;
1046
0
            i.format = fmt;
1047
0
            i.fsaa = fsaa;
1048
0
            i.depthBufferPoolId = depthBufferPoolId;
1049
0
            mShadowTextureConfigDirty = true;
1050
0
        }
1051
0
    }
1052
0
}
1053
//---------------------------------------------------------------------
1054
const TexturePtr& SceneManager::TextureShadowRenderer::getShadowTexture(size_t shadowIndex)
1055
0
{
1056
0
    mSceneManager->ensureShadowTexturesCreated();
1057
1058
0
    OgreAssert(shadowIndex < mShadowTextures.size(), "");
1059
0
    return mShadowTextures[shadowIndex];
1060
0
}
1061
1062
void SceneManager::TextureShadowRenderer::resolveShadowTexture(TextureUnitState* tu, size_t shadowIndex, size_t shadowTexUnitIndex) const
1063
0
{
1064
0
    TexturePtr shadowTex;
1065
0
    if (shadowIndex < mShadowTextures.size())
1066
0
    {
1067
0
        shadowTex = mShadowTextures[shadowIndex];
1068
        // Enable projective texturing if fixed-function, but also need to
1069
        // disable it explicitly for program pipeline.
1070
0
        tu->setProjectiveTexturing(!tu->getParent()->hasVertexProgram(), mShadowTextureCameras[shadowIndex]);
1071
0
    }
1072
0
    else
1073
0
    {
1074
        // Use fallback no-shadow texture
1075
        // no projection since all uniform colour anyway
1076
0
        shadowTex = mNoShadowTexture;
1077
0
        tu->setProjectiveTexturing(false);
1078
0
    }
1079
1080
0
    tu->_setTexturePtr(shadowTex);
1081
1082
    // set up projectors for all layers, fixed function does not support layered textures anyway
1083
0
    size_t layers = std::max(shadowTex->getDepth(), shadowTex->getNumFaces());
1084
0
    layers = std::min(layers, mShadowTextureCameras.size() - shadowIndex); // clamp
1085
0
    for (size_t l = 0; l < layers; ++l)
1086
0
        mSceneManager->mAutoParamDataSource->setTextureProjector(mShadowTextureCameras[shadowIndex + l],
1087
0
                                                                 shadowTexUnitIndex + l);
1088
0
}
1089
1090
namespace
1091
{
1092
/** Default sorting routine which sorts lights which cast shadows
1093
to the front of a list, sub-sorting by distance.
1094
1095
Since shadow textures are generated from lights based on the
1096
frustum rather than individual objects, a shadow and camera-wise sort is
1097
required to pick the best lights near the start of the list. Up to
1098
the number of shadow textures will be generated from this.
1099
*/
1100
struct lightsForShadowTextureLess
1101
{
1102
    bool operator()(const Light* l1, const Light* l2) const
1103
0
    {
1104
0
        if (l1 == l2)
1105
0
            return false;
1106
1107
        // sort shadow casting lights ahead of non-shadow casting
1108
0
        if (l1->getCastShadows() != l2->getCastShadows())
1109
0
        {
1110
0
            return l1->getCastShadows();
1111
0
        }
1112
1113
        // otherwise sort by distance (directional lights will have 0 here)
1114
0
        return l1->tempSquareDist < l2->tempSquareDist;
1115
0
    }
1116
};
1117
} // namespace
1118
1119
void SceneManager::TextureShadowRenderer::sortLightsAffectingFrustum(LightList& lightList) const
1120
0
{
1121
    // Sort the lights if using texture shadows, since the first 'n' will be
1122
    // used to generate shadow textures and we should pick the most appropriate
1123
1124
0
    ListenerList listenersCopy = mListeners; // copy in case of listeners removing themselves
1125
1126
    // Allow a Listener to override light sorting
1127
    // Reverse iterate so last takes precedence
1128
0
    for (auto ri = listenersCopy.rbegin(); ri != listenersCopy.rend(); ++ri)
1129
0
    {
1130
0
        if((*ri)->sortLightsAffectingFrustum(lightList))
1131
0
            return;
1132
0
    }
1133
1134
    // default sort (stable to preserve directional light ordering
1135
0
    std::stable_sort(lightList.begin(), lightList.end(), lightsForShadowTextureLess());
1136
0
}
1137
//---------------------------------------------------------------------
1138
void SceneManager::TextureShadowRenderer::fireShadowTexturesUpdated(size_t numberOfShadowTextures)
1139
0
{
1140
0
    ListenerList listenersCopy = mListeners;
1141
1142
0
    for (auto *l : listenersCopy)
1143
0
    {
1144
0
        l->shadowTexturesUpdated(numberOfShadowTextures);
1145
0
    }
1146
0
}
1147
//---------------------------------------------------------------------
1148
void SceneManager::TextureShadowRenderer::fireShadowTexturesPreCaster(Light* light, Camera* camera, size_t iteration)
1149
0
{
1150
0
    auto listenersCopy = mListeners;
1151
0
    for (auto l : listenersCopy)
1152
0
    {
1153
0
        l->shadowTextureCasterPreViewProj(light, camera, iteration);
1154
0
    }
1155
0
}
1156
//---------------------------------------------------------------------
1157
void SceneManager::TextureShadowRenderer::fireShadowTexturesPreReceiver(Light* light, Frustum* f)
1158
0
{
1159
0
    ListenerList listenersCopy = mListeners;
1160
0
    for (auto *l : listenersCopy)
1161
0
    {
1162
0
        l->shadowTextureReceiverPreViewProj(light, f);
1163
0
    }
1164
0
}
1165
}