Coverage Report

Created: 2026-05-30 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreTechnique.cpp
Line
Count
Source
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
#include "OgreStableHeaders.h"
29
#include "OgreMaterial.h"
30
31
32
namespace Ogre {
33
    //-----------------------------------------------------------------------------
34
    Technique::Technique(Material* parent)
35
3
        : mParent(parent), mIlluminationPassesCompilationPhase(IPS_NOT_COMPILED), mIsSupported(false), mLodIndex(0), mSchemeIndex(0)
36
3
    {
37
        // See above, defaults to unsupported until examined
38
3
    }
39
    //-----------------------------------------------------------------------------
40
    Technique::Technique(Material* parent, const Technique& oth)
41
0
        : mParent(parent), mLodIndex(0), mSchemeIndex(0)
42
0
    {
43
        // Copy using operator=
44
0
        *this = oth;
45
0
    }
46
    //-----------------------------------------------------------------------------
47
    Technique::~Technique()
48
0
    {
49
0
        removeAllPasses();
50
0
        clearIlluminationPasses();
51
0
    }
52
    //-----------------------------------------------------------------------------
53
    bool Technique::isSupported(void) const
54
2
    {
55
2
        return mIsSupported;
56
2
    }
57
    //-----------------------------------------------------------------------------
58
    size_t Technique::calculateSize(void) const
59
0
    {
60
0
        size_t memSize = 0;
61
62
        // Tally up passes
63
0
        for (auto *p : mPasses)
64
0
        {
65
0
            memSize += p->calculateSize();
66
0
        }
67
0
        return memSize;
68
0
    }
69
    //-----------------------------------------------------------------------------
70
    String Technique::_compile(bool autoManageTextureUnits)
71
0
    {
72
0
        StringStream errors;
73
74
0
        mIsSupported = checkGPURules(errors) && checkHardwareSupport(autoManageTextureUnits, errors);
75
76
        // Compile for categorised illumination on demand
77
0
        clearIlluminationPasses();
78
0
        mIlluminationPassesCompilationPhase = IPS_NOT_COMPILED;
79
80
0
        return errors.str();
81
82
0
    }
83
    //---------------------------------------------------------------------
84
    bool Technique::checkHardwareSupport(bool autoManageTextureUnits, StringStream& compileErrors)
85
0
    {
86
        // Go through each pass, checking requirements
87
0
        Passes::iterator i;
88
0
        unsigned short passNum = 0;
89
0
        const RenderSystemCapabilities* caps = nullptr;
90
0
        if (auto rs = Root::getSingleton().getRenderSystem())
91
0
            caps = rs->getCapabilities();
92
0
        else
93
0
        {
94
0
            static RenderSystemCapabilities nullCaps;
95
0
            caps = &nullCaps;
96
0
        }
97
98
0
        unsigned short numTexUnits = caps->getNumTextureUnits();
99
0
        for (i = mPasses.begin(); i != mPasses.end(); ++i, ++passNum)
100
0
        {
101
0
            Pass* currPass = *i;
102
            // Adjust pass index
103
0
            currPass->_notifyIndex(passNum);
104
105
0
            const char* err = 0;
106
107
0
            if(currPass->getLineWidth() != 1 && !caps->hasCapability(RSC_WIDE_LINES))
108
0
                err = "line_width > 1";
109
0
            else if(currPass->getPointSize() != 1 && !caps->hasCapability(RSC_POINT_SPRITES))
110
0
                err = "point_size > 1";
111
112
0
            if(err)
113
0
            {
114
0
                compileErrors << "Pass " << passNum << ": " << err << " not supported by RenderSystem";
115
0
                return false;
116
0
            }
117
118
            // Check a few fixed-function options in texture layers
119
0
            size_t texUnit = 0;
120
0
            for(const TextureUnitState* tex : currPass->getTextureUnitStates())
121
0
            {
122
0
                if ((tex->getTextureType() == TEX_TYPE_3D) && !caps->hasCapability(RSC_TEXTURE_3D))
123
0
                    err = "Volume";
124
125
0
                if ((tex->getTextureType() == TEX_TYPE_2D_ARRAY) && !caps->hasCapability(RSC_TEXTURE_2D_ARRAY))
126
0
                    err = "Array";
127
128
0
                if (err)
129
0
                {
130
                    // Fail
131
0
                    compileErrors << "Pass " << passNum << " Tex " << texUnit << ": " << err
132
0
                                    << " textures not supported by RenderSystem";
133
0
                    return false;
134
0
                }
135
0
                ++texUnit;
136
0
            }
137
138
            // Check texture unit requirements
139
0
            size_t numTexUnitsRequested = currPass->getNumTextureUnitStates();
140
            // Don't trust getNumTextureUnits for programmable
141
0
            if(!currPass->hasFragmentProgram())
142
0
            {
143
#if defined(OGRE_PRETEND_TEXTURE_UNITS) && OGRE_PRETEND_TEXTURE_UNITS > 0
144
                numTexUnits = std::min(numTexUnits, OGRE_PRETEND_TEXTURE_UNITS);
145
#endif
146
0
                if (numTexUnitsRequested > numTexUnits)
147
0
                {
148
0
                    if (!autoManageTextureUnits)
149
0
                    {
150
                        // The user disabled auto pass split
151
0
                        compileErrors << "Pass " << passNum <<
152
0
                            ": Too many texture units for the current hardware and no splitting allowed";
153
0
                        return false;
154
0
                    }
155
0
                    else if (currPass->hasVertexProgram())
156
0
                    {
157
                        // Can't do this one, and can't split a programmable pass
158
0
                        compileErrors << "Pass " << passNum <<
159
0
                            ": Too many texture units for the current hardware and "
160
0
                            "cannot split programmable passes";
161
0
                        return false;
162
0
                    }
163
0
                }
164
165
                // We're ok on operations, now we need to check # texture units
166
167
                    // Keep splitting this pass so long as units requested > gpu units
168
0
                while (numTexUnitsRequested > numTexUnits)
169
0
                {
170
                    // chop this pass into many passes
171
0
                    currPass = currPass->_split(numTexUnits);
172
0
                    numTexUnitsRequested = currPass->getNumTextureUnitStates();
173
                    // Advance pass number
174
0
                    ++passNum;
175
                    // Reset iterator
176
0
                    i = mPasses.begin() + passNum;
177
                    // Move the new pass to the right place (will have been created
178
                    // at the end, may be other passes in between)
179
0
                    assert(mPasses.back() == currPass);
180
0
                    std::copy_backward(i, (mPasses.end() - 1), mPasses.end());
181
0
                    *i = currPass;
182
                    // Adjust pass index
183
0
                    currPass->_notifyIndex(passNum);
184
0
                }
185
0
            }
186
187
            // try to catch user missing a program early on
188
0
            if (!caps->hasCapability(RSC_FIXED_FUNCTION) && currPass->isProgrammable() &&
189
0
                !currPass->hasGpuProgram(GPT_COMPUTE_PROGRAM))
190
0
            {
191
0
                if ((!currPass->hasVertexProgram() && !currPass->hasGpuProgram(GPT_MESH_PROGRAM)) ||
192
0
                    (!currPass->hasFragmentProgram() && !currPass->hasGeometryProgram()))
193
0
                {
194
0
                    compileErrors << "Pass " << passNum << ": RenderSystem requires both vertex and fragment programs";
195
0
                    return false;
196
0
                }
197
0
            }
198
199
            //Check compilation errors for all program types.
200
0
            for (int t = 0; t < GPT_COUNT; t++)
201
0
            {
202
0
                GpuProgramType programType = GpuProgramType(t);
203
0
                if (currPass->hasGpuProgram(programType))
204
0
                {
205
0
                    GpuProgramPtr program = currPass->getGpuProgram(programType);
206
0
                    if (!program->isSupported())
207
0
                    {
208
0
                        compileErrors << "Pass " << passNum <<
209
0
                            ": " << GpuProgram::getProgramTypeName(programType) + " program " << program->getName()
210
0
                            << " cannot be used - ";
211
0
                        if (program->hasCompileError() && program->getSource().empty())
212
0
                            compileErrors << "resource not found";
213
0
                        else if (program->hasCompileError())
214
0
                            compileErrors << "compile error";
215
0
                        else
216
0
                            compileErrors << "not supported";
217
218
0
                        return false;
219
0
                    }
220
0
                }
221
0
            }
222
0
        }
223
        // If we got this far, we're ok
224
0
        return true;
225
0
    }
226
    //---------------------------------------------------------------------
227
    bool Technique::checkGPURules(StringStream& errors)
228
0
    {
229
0
        const RenderSystemCapabilities* caps = nullptr;
230
0
        if (auto rs = Root::getSingleton().getRenderSystem())
231
0
            caps = rs->getCapabilities();
232
233
0
        if (!caps && (!mGPUVendorRules.empty() || !mGPUDeviceNameRules.empty()))
234
0
        {
235
0
            errors << "GPU rules failed because the RenderSystem is NULL";
236
0
            return false;
237
0
        }
238
239
0
        StringStream includeRules;
240
0
        bool includeRulesPresent = false;
241
0
        bool includeRuleMatched = false;
242
243
        // Check vendors first
244
0
        for (auto& r : mGPUVendorRules)
245
0
        {
246
0
            if (r.includeOrExclude == INCLUDE)
247
0
            {
248
0
                includeRulesPresent = true;
249
0
                includeRules << caps->vendorToString(r.vendor) << " ";
250
0
                if (r.vendor == caps->getVendor())
251
0
                    includeRuleMatched = true;
252
0
            }
253
0
            else // EXCLUDE
254
0
            {
255
0
                if (r.vendor == caps->getVendor())
256
0
                {
257
0
                    errors << "Excluded GPU vendor: " << caps->vendorToString(r.vendor)
258
0
                        << std::endl;
259
0
                    return false;
260
0
                }
261
262
0
            }
263
0
        }
264
265
0
        if (includeRulesPresent && !includeRuleMatched)
266
0
        {
267
0
            errors << "Failed to match GPU vendor: " << includeRules.str( )
268
0
                << std::endl;
269
0
            return false;
270
0
        }
271
272
        // now check device names
273
0
        includeRules.str(BLANKSTRING);
274
0
        includeRulesPresent = false;
275
0
        includeRuleMatched = false;
276
277
0
        for (auto& r : mGPUDeviceNameRules)
278
0
        {
279
0
            if (r.includeOrExclude == INCLUDE)
280
0
            {
281
0
                includeRulesPresent = true;
282
0
                includeRules << r.devicePattern << " ";
283
0
                if (StringUtil::match(caps->getDeviceName(), r.devicePattern, r.caseSensitive))
284
0
                    includeRuleMatched = true;
285
0
            }
286
0
            else // EXCLUDE
287
0
            {
288
0
                if (StringUtil::match(caps->getDeviceName(), r.devicePattern, r.caseSensitive))
289
0
                {
290
0
                    errors << "Excluded GPU device: " << r.devicePattern
291
0
                        << std::endl;
292
0
                    return false;
293
0
                }
294
295
0
            }
296
0
        }
297
298
0
        if (includeRulesPresent && !includeRuleMatched)
299
0
        {
300
0
            errors << "Failed to match GPU device: " << includeRules.str( )
301
0
                << std::endl;
302
0
            return false;
303
0
        }
304
305
        // passed
306
0
        return true;
307
0
    }
308
    //-----------------------------------------------------------------------------
309
    Pass* Technique::createPass(void)
310
1
    {
311
1
        Pass* newPass = OGRE_NEW Pass(this, static_cast<unsigned short>(mPasses.size()));
312
1
        mPasses.push_back(newPass);
313
1
        return newPass;
314
1
    }
315
    //-----------------------------------------------------------------------------
316
    Pass* Technique::getPass(const String& name) const
317
0
    {
318
        // iterate through techniques to find a match
319
0
        for (Pass *p : mPasses) {
320
0
            if (p->getName() == name )
321
0
                return p;
322
0
        }
323
324
0
        return (Pass *)0;
325
0
    }
326
    //-----------------------------------------------------------------------------
327
    void Technique::removePass(unsigned short index)
328
0
    {
329
0
        assert(index < mPasses.size() && "Index out of bounds");
330
0
        Passes::iterator i = mPasses.begin() + index;
331
0
        (*i)->queueForDeletion();
332
0
        i = mPasses.erase(i);
333
        // Adjust passes index
334
0
        for (; i != mPasses.end(); ++i, ++index)
335
0
        {
336
0
            (*i)->_notifyIndex(index);
337
0
        }
338
0
    }
339
    //-----------------------------------------------------------------------------
340
    void Technique::removeAllPasses(void)
341
2
    {
342
2
        for (auto *p : mPasses)
343
0
        {
344
0
            p->queueForDeletion();
345
0
        }
346
2
        mPasses.clear();
347
2
    }
348
349
    //-----------------------------------------------------------------------------
350
    bool Technique::movePass(const unsigned short sourceIndex, const unsigned short destinationIndex)
351
0
    {
352
0
        bool moveSuccessful = false;
353
354
        // don't move the pass if source == destination
355
0
        if (sourceIndex == destinationIndex) return true;
356
357
0
        if( (sourceIndex < mPasses.size()) && (destinationIndex < mPasses.size()))
358
0
        {
359
0
            Passes::iterator i = mPasses.begin() + sourceIndex;
360
            //Passes::iterator DestinationIterator = mPasses.begin() + destinationIndex;
361
362
0
            Pass* pass = (*i);
363
0
            mPasses.erase(i);
364
365
0
            i = mPasses.begin() + destinationIndex;
366
367
0
            mPasses.insert(i, pass);
368
369
            // Adjust passes index
370
0
            unsigned short beginIndex, endIndex;
371
0
            if (destinationIndex > sourceIndex)
372
0
            {
373
0
                beginIndex = sourceIndex;
374
0
                endIndex = destinationIndex;
375
0
            }
376
0
            else
377
0
            {
378
0
                beginIndex = destinationIndex;
379
0
                endIndex = sourceIndex;
380
0
            }
381
0
            for (unsigned short index = beginIndex; index <= endIndex; ++index)
382
0
            {
383
0
                mPasses[index]->_notifyIndex(index);
384
0
            }
385
0
            moveSuccessful = true;
386
0
        }
387
388
0
        return moveSuccessful;
389
0
    }
390
391
    //-----------------------------------------------------------------------------
392
    const Technique::PassIterator Technique::getPassIterator(void)
393
0
    {
394
0
        return PassIterator(mPasses.begin(), mPasses.end());
395
0
    }
396
    //-----------------------------------------------------------------------------
397
    Technique& Technique::operator=(const Technique& rhs)
398
2
    {
399
2
        if (this == &rhs)
400
0
            return *this;
401
402
2
        mName = rhs.mName;
403
2
        this->mIsSupported = rhs.mIsSupported;
404
2
        this->mLodIndex = rhs.mLodIndex;
405
2
        this->mSchemeIndex = rhs.mSchemeIndex;
406
2
        this->mShadowCasterMaterial = rhs.mShadowCasterMaterial;
407
2
        this->mShadowCasterMaterialName = rhs.mShadowCasterMaterialName;
408
2
        this->mShadowReceiverMaterial = rhs.mShadowReceiverMaterial;
409
2
        this->mShadowReceiverMaterialName = rhs.mShadowReceiverMaterialName;
410
2
        this->mGPUVendorRules = rhs.mGPUVendorRules;
411
2
        this->mGPUDeviceNameRules = rhs.mGPUDeviceNameRules;
412
413
        // copy passes
414
2
        removeAllPasses();
415
2
        for (auto *rp : rhs.mPasses)
416
2
        {
417
2
            Pass* p = OGRE_NEW Pass(this, rp->getIndex(), *(rp));
418
2
            mPasses.push_back(p);
419
2
        }
420
        // Compile for categorised illumination on demand
421
2
        clearIlluminationPasses();
422
2
        mIlluminationPassesCompilationPhase = IPS_NOT_COMPILED;
423
2
        return *this;
424
2
    }
425
    //-----------------------------------------------------------------------------
426
    bool Technique::isTransparent(void) const
427
0
    {
428
0
        if (mPasses.empty())
429
0
        {
430
0
            return false;
431
0
        }
432
0
        else
433
0
        {
434
            // Base decision on the transparency of the first pass
435
0
            return mPasses[0]->isTransparent();
436
0
        }
437
0
    }
438
    //-----------------------------------------------------------------------------
439
    bool Technique::isTransparentSortingEnabled(void) const
440
0
    {
441
0
        if (mPasses.empty())
442
0
        {
443
0
            return true;
444
0
        }
445
0
        else
446
0
        {
447
            // Base decision on the transparency of the first pass
448
0
            return mPasses[0]->getTransparentSortingEnabled();
449
0
        }
450
0
    }
451
    //-----------------------------------------------------------------------------
452
    bool Technique::isTransparentSortingForced(void) const
453
0
    {
454
0
        if (mPasses.empty())
455
0
        {
456
0
            return false;
457
0
        }
458
0
        else
459
0
        {
460
            // Base decision on the first pass
461
0
            return mPasses[0]->getTransparentSortingForced();
462
0
        }
463
0
    }
464
    //-----------------------------------------------------------------------------
465
    bool Technique::isDepthWriteEnabled(void) const
466
0
    {
467
0
        if (mPasses.empty())
468
0
        {
469
0
            return false;
470
0
        }
471
0
        else
472
0
        {
473
            // Base decision on the depth settings of the first pass
474
0
            return mPasses[0]->getDepthWriteEnabled();
475
0
        }
476
0
    }
477
    //-----------------------------------------------------------------------------
478
    bool Technique::isDepthCheckEnabled(void) const
479
0
    {
480
0
        if (mPasses.empty())
481
0
        {
482
0
            return false;
483
0
        }
484
0
        else
485
0
        {
486
            // Base decision on the depth settings of the first pass
487
0
            return mPasses[0]->getDepthCheckEnabled();
488
0
        }
489
0
    }
490
    //-----------------------------------------------------------------------------
491
    bool Technique::hasColourWriteDisabled(void) const
492
0
    {
493
0
        if (mPasses.empty())
494
0
        {
495
0
            return true;
496
0
        }
497
0
        else
498
0
        {
499
            // Base decision on the colour write settings of the first pass
500
0
            return !mPasses[0]->getColourWriteEnabled();
501
0
        }
502
0
    }
503
    //-----------------------------------------------------------------------------
504
    void Technique::_prepare(void)
505
0
    {
506
0
        assert (mIsSupported && "This technique is not supported");
507
        // Load each pass
508
0
        for (auto *p : mPasses)
509
0
        {
510
0
            p->_prepare();
511
0
        }
512
513
0
        for (auto *i : mIlluminationPasses)
514
0
        {
515
0
            if(i->pass != i->originalPass)
516
0
                i->pass->_prepare();
517
0
        }
518
0
    }
519
    //-----------------------------------------------------------------------------
520
    void Technique::_unprepare(void)
521
0
    {
522
        // Unload each pass
523
0
        for (auto *p : mPasses)
524
0
        {
525
0
            p->_unprepare();
526
0
        }
527
0
    }
528
    //-----------------------------------------------------------------------------
529
    void Technique::_load(void)
530
0
    {
531
        // Load each pass
532
0
        for (auto *p : mPasses)
533
0
        {
534
0
            p->_load();
535
0
        }
536
537
0
        for (auto *i : mIlluminationPasses)
538
0
        {
539
0
            if(i->pass != i->originalPass)
540
0
                i->pass->_load();
541
0
        }
542
543
0
        if (!mShadowCasterMaterial && !mShadowCasterMaterialName.empty())
544
0
        {
545
            // in case we could not get material as it wasn't yet parsed/existent at that time.
546
0
            mShadowCasterMaterial = MaterialManager::getSingleton().getByName(mShadowCasterMaterialName);
547
0
        }
548
549
0
        if (mShadowCasterMaterial && mShadowCasterMaterial.get() != getParent())
550
0
        {
551
0
            mShadowCasterMaterial->load();
552
0
        }
553
554
0
        if(!mShadowReceiverMaterial && !mShadowReceiverMaterialName.empty())
555
0
        {
556
            // in case we could not get material as it wasn't yet parsed/existent at that time.
557
0
            mShadowReceiverMaterial = MaterialManager::getSingleton().getByName(mShadowReceiverMaterialName);
558
0
        }
559
560
0
        if (mShadowReceiverMaterial && mShadowReceiverMaterial.get() != getParent())
561
0
        {
562
0
            mShadowReceiverMaterial->load();
563
0
        }
564
0
    }
565
    //-----------------------------------------------------------------------------
566
    void Technique::_unload(void)
567
0
    {
568
        // Unload each pass
569
0
        for (auto *p : mPasses)
570
0
        {
571
0
            p->_unload();
572
0
        }
573
0
    }
574
    //-----------------------------------------------------------------------------
575
    bool Technique::isLoaded(void) const
576
0
    {
577
        // Only supported technique will be loaded
578
0
        return mParent->isLoaded() && mIsSupported;
579
0
    }
580
    //-----------------------------------------------------------------------
581
1
    #define ALL_PASSES(fncall) for(auto p : mPasses) p->fncall
582
0
    void Technique::setPointSize(Real ps) { ALL_PASSES(setPointSize(ps)); }
583
    //-----------------------------------------------------------------------
584
0
    void Technique::setAmbient(float red, float green, float blue) { setAmbient(ColourValue(red, green, blue)); }
585
    //-----------------------------------------------------------------------
586
0
    void Technique::setAmbient(const ColourValue& ambient) { ALL_PASSES(setAmbient(ambient)); }
587
    //-----------------------------------------------------------------------
588
    void Technique::setDiffuse(float red, float green, float blue, float alpha)
589
0
    {
590
0
        ALL_PASSES(setDiffuse(red, green, blue, alpha));
591
0
    }
592
    //-----------------------------------------------------------------------
593
0
    void Technique::setDiffuse(const ColourValue& diffuse) { setDiffuse(diffuse.r, diffuse.g, diffuse.b, diffuse.a); }
594
    //-----------------------------------------------------------------------
595
    void Technique::setSpecular(float red, float green, float blue, float alpha)
596
0
    {
597
0
        ALL_PASSES(setSpecular(red, green, blue, alpha));
598
0
    }
599
    //-----------------------------------------------------------------------
600
    void Technique::setSpecular(const ColourValue& specular)
601
0
    {
602
0
        setSpecular(specular.r, specular.g, specular.b, specular.a);
603
0
    }
604
    //-----------------------------------------------------------------------
605
0
    void Technique::setShininess(Real val) { ALL_PASSES(setShininess(val)); }
606
    //-----------------------------------------------------------------------
607
    void Technique::setSelfIllumination(float red, float green, float blue)
608
0
    {
609
0
        setSelfIllumination(ColourValue(red, green, blue));
610
0
    }
611
    //-----------------------------------------------------------------------
612
0
    void Technique::setSelfIllumination(const ColourValue& selfIllum) { ALL_PASSES(setSelfIllumination(selfIllum)); }
613
    //-----------------------------------------------------------------------
614
0
    void Technique::setDepthCheckEnabled(bool enabled) { ALL_PASSES(setDepthCheckEnabled(enabled)); }
615
    //-----------------------------------------------------------------------
616
0
    void Technique::setDepthWriteEnabled(bool enabled) { ALL_PASSES(setDepthWriteEnabled(enabled)); }
617
    //-----------------------------------------------------------------------
618
0
    void Technique::setDepthFunction(CompareFunction func) { ALL_PASSES(setDepthFunction(func)); }
619
    //-----------------------------------------------------------------------
620
0
    void Technique::setColourWriteEnabled(bool enabled) { ALL_PASSES(setColourWriteEnabled(enabled)); }
621
    //-----------------------------------------------------------------------
622
    void Technique::setColourWriteEnabled(bool red, bool green, bool blue, bool alpha)
623
0
    {
624
0
        ALL_PASSES(setColourWriteEnabled(red, green, blue, alpha));
625
0
    }
626
    //-----------------------------------------------------------------------
627
0
    void Technique::setCullingMode(CullingMode mode) { ALL_PASSES(setCullingMode(mode)); }
628
    //-----------------------------------------------------------------------
629
0
    void Technique::setManualCullingMode(ManualCullingMode mode) { ALL_PASSES(setManualCullingMode(mode)); }
630
    //-----------------------------------------------------------------------
631
1
    void Technique::setLightingEnabled(bool enabled) { ALL_PASSES(setLightingEnabled(enabled)); }
632
    //-----------------------------------------------------------------------
633
0
    void Technique::setShadingMode(ShadeOptions mode) { ALL_PASSES(setShadingMode(mode)); }
634
    //-----------------------------------------------------------------------
635
    void Technique::setFog(bool overrideScene, FogMode mode, const ColourValue& colour, Real expDensity,
636
                           Real linearStart, Real linearEnd)
637
0
    {
638
0
        ALL_PASSES(setFog(overrideScene, mode, colour, expDensity, linearStart, linearEnd));
639
0
    }
640
    //-----------------------------------------------------------------------
641
    void Technique::setDepthBias(float constantBias, float slopeScaleBias)
642
0
    {
643
0
        ALL_PASSES(setDepthBias(constantBias, slopeScaleBias));
644
0
    }
645
    //-----------------------------------------------------------------------
646
    void Technique::setTextureFiltering(TextureFilterOptions filterType)
647
0
    {
648
0
        ALL_PASSES(setTextureFiltering(filterType));
649
0
    }
650
    // --------------------------------------------------------------------
651
0
    void Technique::setTextureAnisotropy(unsigned int maxAniso) { ALL_PASSES(setTextureAnisotropy(maxAniso)); }
652
    // --------------------------------------------------------------------
653
0
    void Technique::setSceneBlending(const SceneBlendType sbt) { ALL_PASSES(setSceneBlending(sbt)); }
654
    // --------------------------------------------------------------------
655
    void Technique::setSeparateSceneBlending(const SceneBlendType sbt, const SceneBlendType sbta)
656
0
    {
657
0
        ALL_PASSES(setSeparateSceneBlending(sbt, sbta));
658
0
    }
659
    // --------------------------------------------------------------------
660
    void Technique::setSceneBlending(const SceneBlendFactor sourceFactor, const SceneBlendFactor destFactor)
661
0
    {
662
0
        ALL_PASSES(setSceneBlending(sourceFactor, destFactor));
663
0
    }
664
    // --------------------------------------------------------------------
665
    void Technique::setSeparateSceneBlending( const SceneBlendFactor sourceFactor, const SceneBlendFactor destFactor, const SceneBlendFactor sourceFactorAlpha, const SceneBlendFactor destFactorAlpha)
666
0
    {
667
0
        ALL_PASSES(setSeparateSceneBlending(sourceFactor, destFactor, sourceFactorAlpha, destFactorAlpha));
668
0
    }
669
    #undef ALL_PASSES
670
671
    // --------------------------------------------------------------------
672
    void Technique::setName(const String& name)
673
0
    {
674
0
        mName = name;
675
0
    }
676
677
678
    //-----------------------------------------------------------------------
679
    void Technique::_notifyNeedsRecompile(void)
680
0
    {
681
        // Disable require to recompile when splitting illumination passes
682
0
        if (mIlluminationPassesCompilationPhase != IPS_COMPILE_DISABLED)
683
0
        {
684
0
            mParent->_notifyNeedsRecompile();
685
0
        }
686
0
    }
687
    //-----------------------------------------------------------------------
688
    void Technique::setLodIndex(unsigned short index)
689
0
    {
690
0
        mLodIndex = index;
691
0
        _notifyNeedsRecompile();
692
0
    }
693
    //-----------------------------------------------------------------------
694
    void Technique::setSchemeName(const String& schemeName)
695
0
    {
696
0
        mSchemeIndex = MaterialManager::getSingleton()._getSchemeIndex(schemeName);
697
0
        _notifyNeedsRecompile();
698
0
    }
699
    //-----------------------------------------------------------------------
700
    const String& Technique::getSchemeName(void) const
701
0
    {
702
0
        return MaterialManager::getSingleton()._getSchemeName(mSchemeIndex);
703
0
    }
704
    //-----------------------------------------------------------------------
705
    unsigned short Technique::_getSchemeIndex(void) const
706
0
    {
707
0
        return mSchemeIndex;
708
0
    }
709
    //---------------------------------------------------------------------
710
    bool Technique::checkManuallyOrganisedIlluminationPasses()
711
0
    {
712
        // first check whether all passes have manually assigned illumination
713
0
        for (auto *p : mPasses)
714
0
        {
715
0
            if (p->getIlluminationStage() == IS_UNKNOWN)
716
0
                return false;
717
0
        }
718
719
        // ok, all manually controlled, so just use that
720
0
        for (auto *p : mPasses)
721
0
        {
722
0
            IlluminationPass* iPass = OGRE_NEW IlluminationPass();
723
0
            iPass->destroyOnShutdown = false;
724
0
            iPass->originalPass = iPass->pass = p;
725
0
            iPass->stage = p->getIlluminationStage();
726
0
            mIlluminationPasses.push_back(iPass);
727
0
        }
728
729
0
        return true;
730
0
    }
731
    //-----------------------------------------------------------------------
732
    void Technique::_compileIlluminationPasses(void)
733
0
    {
734
0
        clearIlluminationPasses();
735
736
0
        if (!checkManuallyOrganisedIlluminationPasses())
737
0
        {
738
            // Build based on our own heuristics
739
740
0
            Passes::iterator i, iend;
741
0
            iend = mPasses.end();
742
0
            i = mPasses.begin();
743
744
0
            IlluminationStage iStage = IS_AMBIENT;
745
746
0
            bool haveAmbient = false;
747
0
            while (i != iend)
748
0
            {
749
0
                IlluminationPass* iPass;
750
0
                Pass* p = *i;
751
0
                switch(iStage)
752
0
                {
753
0
                case IS_AMBIENT:
754
                    // Keep looking for ambient only
755
0
                    if (p->isAmbientOnly())
756
0
                    {
757
                        // Add this pass wholesale
758
0
                        iPass = OGRE_NEW IlluminationPass();
759
0
                        iPass->destroyOnShutdown = false;
760
0
                        iPass->originalPass = iPass->pass = p;
761
0
                        iPass->stage = iStage;
762
0
                        mIlluminationPasses.push_back(iPass);
763
0
                        haveAmbient = true;
764
                        // progress to next pass
765
0
                        ++i;
766
0
                    }
767
0
                    else
768
0
                    {
769
                        // Split off any ambient part
770
0
                        if (p->getAmbient() != ColourValue::Black ||
771
0
                            p->getSelfIllumination() != ColourValue::Black ||
772
0
                            p->getAlphaRejectFunction() != CMPF_ALWAYS_PASS)
773
0
                        {
774
                            // Copy existing pass
775
0
                            Pass* newPass = OGRE_NEW Pass(this, p->getIndex(), *p);
776
0
                            if (newPass->getAlphaRejectFunction() != CMPF_ALWAYS_PASS)
777
0
                            {
778
                                // Alpha rejection passes must retain their transparency, so
779
                                // we allow the texture units, but override the colour functions
780
0
                                for(auto *s : newPass->getTextureUnitStates())
781
0
                                {
782
0
                                    s->setColourOperationEx(LBX_SOURCE1, LBS_CURRENT);
783
0
                                }
784
0
                            }
785
0
                            else
786
0
                            {
787
                                // Remove any texture units
788
0
                                newPass->removeAllTextureUnitStates();
789
0
                            }
790
                            // Remove any fragment program
791
0
                            if (newPass->hasFragmentProgram())
792
0
                                newPass->setFragmentProgram("");
793
                            // We have to leave vertex program alone (if any) and
794
                            // just trust that the author is using light bindings, which
795
                            // we will ensure there are none in the ambient pass
796
0
                            newPass->setDiffuse(0, 0, 0, newPass->getDiffuse().a);  // Preserving alpha
797
0
                            newPass->setSpecular(ColourValue::Black);
798
799
                            // Calculate hash value for new pass, because we are compiling
800
                            // illumination passes on demand, which will loss hash calculate
801
                            // before it add to render queue first time.
802
0
                            newPass->_recalculateHash();
803
804
0
                            iPass = OGRE_NEW IlluminationPass();
805
0
                            iPass->destroyOnShutdown = true;
806
0
                            iPass->originalPass = p;
807
0
                            iPass->pass = newPass;
808
0
                            iPass->stage = iStage;
809
810
0
                            mIlluminationPasses.push_back(iPass);
811
0
                            haveAmbient = true;
812
813
0
                        }
814
815
0
                        if (!haveAmbient)
816
0
                        {
817
                            // Make up a new basic pass
818
0
                            Pass* newPass = OGRE_NEW Pass(this, p->getIndex());
819
0
                            newPass->setAmbient(ColourValue::Black);
820
0
                            newPass->setDiffuse(ColourValue::Black);
821
822
                            // Calculate hash value for new pass, because we are compiling
823
                            // illumination passes on demand, which will loss hash calculate
824
                            // before it add to render queue first time.
825
0
                            newPass->_recalculateHash();
826
827
0
                            iPass = OGRE_NEW IlluminationPass();
828
0
                            iPass->destroyOnShutdown = true;
829
0
                            iPass->originalPass = p;
830
0
                            iPass->pass = newPass;
831
0
                            iPass->stage = iStage;
832
0
                            mIlluminationPasses.push_back(iPass);
833
0
                            haveAmbient = true;
834
0
                        }
835
                        // This means we're done with ambients, progress to per-light
836
0
                        iStage = IS_PER_LIGHT;
837
0
                    }
838
0
                    break;
839
0
                case IS_PER_LIGHT:
840
0
                    if (p->getIteratePerLight())
841
0
                    {
842
                        // If this is per-light already, use it directly
843
0
                        iPass = OGRE_NEW IlluminationPass();
844
0
                        iPass->destroyOnShutdown = false;
845
0
                        iPass->originalPass = iPass->pass = p;
846
0
                        iPass->stage = iStage;
847
0
                        mIlluminationPasses.push_back(iPass);
848
                        // progress to next pass
849
0
                        ++i;
850
0
                    }
851
0
                    else
852
0
                    {
853
                        // Split off per-light details (can only be done for one)
854
0
                        if (p->getLightingEnabled() &&
855
0
                            (p->getDiffuse() != ColourValue::Black ||
856
0
                            p->getSpecular() != ColourValue::Black))
857
0
                        {
858
                            // Copy existing pass
859
0
                            Pass* newPass = OGRE_NEW Pass(this, p->getIndex(), *p);
860
0
                            if (newPass->getAlphaRejectFunction() != CMPF_ALWAYS_PASS)
861
0
                            {
862
                                // Alpha rejection passes must retain their transparency, so
863
                                // we allow the texture units, but override the colour functions
864
0
                                for(auto *s : newPass->getTextureUnitStates())
865
0
                                {
866
0
                                    s->setColourOperationEx(LBX_SOURCE1, LBS_CURRENT);
867
0
                                }
868
0
                            }
869
0
                            else
870
0
                            {
871
                                // remove texture units
872
0
                                newPass->removeAllTextureUnitStates();
873
0
                            }
874
                            // remove fragment programs
875
0
                            if (newPass->hasFragmentProgram())
876
0
                                newPass->setFragmentProgram("");
877
                            // Cannot remove vertex program, have to assume that
878
                            // it will process diffuse lights, ambient will be turned off
879
0
                            newPass->setAmbient(ColourValue::Black);
880
0
                            newPass->setSelfIllumination(ColourValue::Black);
881
                            // must be additive
882
0
                            newPass->setSceneBlending(SBF_ONE, SBF_ONE);
883
884
                            // Calculate hash value for new pass, because we are compiling
885
                            // illumination passes on demand, which will loss hash calculate
886
                            // before it add to render queue first time.
887
0
                            newPass->_recalculateHash();
888
889
0
                            iPass = OGRE_NEW IlluminationPass();
890
0
                            iPass->destroyOnShutdown = true;
891
0
                            iPass->originalPass = p;
892
0
                            iPass->pass = newPass;
893
0
                            iPass->stage = iStage;
894
895
0
                            mIlluminationPasses.push_back(iPass);
896
897
0
                        }
898
                        // This means the end of per-light passes
899
0
                        iStage = IS_DECAL;
900
0
                    }
901
0
                    break;
902
0
                case IS_DECAL:
903
                    // We just want a 'lighting off' pass to finish off
904
                    // and only if there are texture units
905
0
                    if (p->getNumTextureUnitStates() > 0)
906
0
                    {
907
0
                        if (!p->getLightingEnabled())
908
0
                        {
909
                            // we assume this pass already combines as required with the scene
910
0
                            iPass = OGRE_NEW IlluminationPass();
911
0
                            iPass->destroyOnShutdown = false;
912
0
                            iPass->originalPass = iPass->pass = p;
913
0
                            iPass->stage = iStage;
914
0
                            mIlluminationPasses.push_back(iPass);
915
0
                        }
916
0
                        else
917
0
                        {
918
                            // Copy the pass and tweak away the lighting parts
919
0
                            Pass* newPass = OGRE_NEW Pass(this, p->getIndex(), *p);
920
0
                            newPass->setAmbient(ColourValue::Black);
921
0
                            newPass->setDiffuse(0, 0, 0, newPass->getDiffuse().a);  // Preserving alpha
922
0
                            newPass->setSpecular(ColourValue::Black);
923
0
                            newPass->setSelfIllumination(ColourValue::Black);
924
0
                            newPass->setLightingEnabled(false);
925
0
                            newPass->setIteratePerLight(false, false);
926
                            // modulate
927
0
                            newPass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO);
928
929
                            // Calculate hash value for new pass, because we are compiling
930
                            // illumination passes on demand, which will loss hash calculate
931
                            // before it add to render queue first time.
932
0
                            newPass->_recalculateHash();
933
934
                            // NB there is nothing we can do about vertex & fragment
935
                            // programs here, so people will just have to make their
936
                            // programs friendly-like if they want to use this technique
937
0
                            iPass = OGRE_NEW IlluminationPass();
938
0
                            iPass->destroyOnShutdown = true;
939
0
                            iPass->originalPass = p;
940
0
                            iPass->pass = newPass;
941
0
                            iPass->stage = iStage;
942
0
                            mIlluminationPasses.push_back(iPass);
943
944
0
                        }
945
0
                    }
946
0
                    ++i; // always increment on decal, since nothing more to do with this pass
947
948
0
                    break;
949
0
                case IS_UNKNOWN:
950
0
                    break;
951
0
                }
952
0
            }
953
0
        }
954
955
0
    }
956
    //-----------------------------------------------------------------------
957
    void Technique::clearIlluminationPasses(void)
958
2
    {
959
2
        if(MaterialManager::getSingletonPtr())
960
2
            MaterialManager::getSingleton()._notifyBeforeIlluminationPassesCleared(this);
961
962
2
        for (auto *i : mIlluminationPasses)
963
0
        {
964
0
            if (i->destroyOnShutdown)
965
0
            {
966
0
                i->pass->queueForDeletion();
967
0
            }
968
0
            OGRE_DELETE i;
969
0
        }
970
2
        mIlluminationPasses.clear();
971
2
    }
972
    //-----------------------------------------------------------------------
973
    const IlluminationPassList&
974
    Technique::getIlluminationPasses(void)
975
0
    {
976
0
        IlluminationPassesState targetState = IPS_COMPILED;
977
0
        if(mIlluminationPassesCompilationPhase != targetState
978
0
        && mIlluminationPassesCompilationPhase != IPS_COMPILE_DISABLED)
979
0
        {
980
            // prevents parent->_notifyNeedsRecompile() call during compile
981
0
            mIlluminationPassesCompilationPhase = IPS_COMPILE_DISABLED;
982
            // Splitting the passes into illumination passes
983
0
            _compileIlluminationPasses();
984
            // Post notification, so that technique owner can post-process created passes
985
0
            if(MaterialManager::getSingletonPtr())
986
0
                MaterialManager::getSingleton()._notifyAfterIlluminationPassesCreated(this);
987
            // Mark that illumination passes compilation finished
988
0
            mIlluminationPassesCompilationPhase = targetState;
989
0
        }
990
991
0
        return mIlluminationPasses;
992
0
    }
993
    //-----------------------------------------------------------------------
994
    const String& Technique::getResourceGroup(void) const
995
0
    {
996
0
        return mParent->getGroup();
997
0
    }
998
    //-----------------------------------------------------------------------
999
    Ogre::MaterialPtr  Technique::getShadowCasterMaterial() const
1000
0
    {
1001
0
        return mShadowCasterMaterial;
1002
0
    }
1003
    //-----------------------------------------------------------------------
1004
    void  Technique::setShadowCasterMaterial(Ogre::MaterialPtr val)
1005
0
    {
1006
0
        if (!val)
1007
0
        {
1008
0
            mShadowCasterMaterial.reset();
1009
0
            mShadowCasterMaterialName.clear();
1010
0
        }
1011
0
        else
1012
0
        {
1013
            // shadow caster material should never receive shadows
1014
0
            val->setReceiveShadows(false); // should we warn if this is not set?
1015
0
            mShadowCasterMaterial = val;
1016
0
            mShadowCasterMaterialName = val->getName();
1017
0
        }
1018
0
    }
1019
    //-----------------------------------------------------------------------
1020
    void  Technique::setShadowCasterMaterial(const Ogre::String &name)
1021
0
    {
1022
0
        setShadowCasterMaterial(MaterialManager::getSingleton().getByName(name));
1023
        // remember the name, even if it is not created yet
1024
0
        mShadowCasterMaterialName = name;
1025
0
    }
1026
    //-----------------------------------------------------------------------
1027
    Ogre::MaterialPtr  Technique::getShadowReceiverMaterial() const
1028
0
    {
1029
0
        return mShadowReceiverMaterial;
1030
0
    }
1031
    //-----------------------------------------------------------------------
1032
    void  Technique::setShadowReceiverMaterial(Ogre::MaterialPtr val)
1033
0
    {
1034
0
        if (!val)
1035
0
        {
1036
0
            mShadowReceiverMaterial.reset();
1037
0
            mShadowReceiverMaterialName.clear();
1038
0
        }
1039
0
        else
1040
0
        {
1041
0
            mShadowReceiverMaterial = val;
1042
0
            mShadowReceiverMaterialName = val->getName();
1043
0
        }
1044
0
    }
1045
    //-----------------------------------------------------------------------
1046
    void  Technique::setShadowReceiverMaterial(const Ogre::String &name)
1047
0
    {
1048
0
        mShadowReceiverMaterialName = name;
1049
0
        mShadowReceiverMaterial = MaterialManager::getSingleton().getByName(name);
1050
0
    }
1051
    //---------------------------------------------------------------------
1052
    void Technique::addGPUVendorRule(GPUVendor vendor, Technique::IncludeOrExclude includeOrExclude)
1053
0
    {
1054
0
        addGPUVendorRule(GPUVendorRule(vendor, includeOrExclude));
1055
0
    }
1056
    //---------------------------------------------------------------------
1057
    void Technique::addGPUVendorRule(const Technique::GPUVendorRule& rule)
1058
0
    {
1059
        // remove duplicates
1060
0
        removeGPUVendorRule(rule.vendor);
1061
0
        mGPUVendorRules.push_back(rule);
1062
0
    }
1063
    //---------------------------------------------------------------------
1064
    void Technique::removeGPUVendorRule(GPUVendor vendor)
1065
0
    {
1066
0
        for (GPUVendorRuleList::iterator i = mGPUVendorRules.begin(); i != mGPUVendorRules.end(); )
1067
0
        {
1068
0
            if (i->vendor == vendor)
1069
0
                i = mGPUVendorRules.erase(i);
1070
0
            else
1071
0
                ++i;
1072
0
        }
1073
0
    }
1074
    //---------------------------------------------------------------------
1075
    Technique::GPUVendorRuleIterator Technique::getGPUVendorRuleIterator() const
1076
0
    {
1077
0
        return GPUVendorRuleIterator(mGPUVendorRules.begin(), mGPUVendorRules.end());
1078
0
    }
1079
    //---------------------------------------------------------------------
1080
    void Technique::addGPUDeviceNameRule(const String& devicePattern,
1081
        Technique::IncludeOrExclude includeOrExclude, bool caseSensitive)
1082
0
    {
1083
0
        addGPUDeviceNameRule(GPUDeviceNameRule(devicePattern, includeOrExclude, caseSensitive));
1084
0
    }
1085
    //---------------------------------------------------------------------
1086
    void Technique::addGPUDeviceNameRule(const Technique::GPUDeviceNameRule& rule)
1087
0
    {
1088
        // remove duplicates
1089
0
        removeGPUDeviceNameRule(rule.devicePattern);
1090
0
        mGPUDeviceNameRules.push_back(rule);
1091
0
    }
1092
    //---------------------------------------------------------------------
1093
    void Technique::removeGPUDeviceNameRule(const String& devicePattern)
1094
0
    {
1095
0
        for (GPUDeviceNameRuleList::iterator i = mGPUDeviceNameRules.begin(); i != mGPUDeviceNameRules.end(); )
1096
0
        {
1097
0
            if (i->devicePattern == devicePattern)
1098
0
                i = mGPUDeviceNameRules.erase(i);
1099
0
            else
1100
0
                ++i;
1101
0
        }
1102
0
    }
1103
    //---------------------------------------------------------------------
1104
    Technique::GPUDeviceNameRuleIterator Technique::getGPUDeviceNameRuleIterator() const
1105
0
    {
1106
0
        return GPUDeviceNameRuleIterator(mGPUDeviceNameRules.begin(), mGPUDeviceNameRules.end());
1107
0
    }
1108
    //---------------------------------------------------------------------
1109
}