File: shadowsbin.cpp

package info (click to toggle)
openmw 0.49.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,992 kB
  • sloc: cpp: 372,479; xml: 2,149; sh: 1,403; python: 797; makefile: 26
file content (227 lines) | stat: -rw-r--r-- 9,177 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include "shadowsbin.hpp"
#include <osg/AlphaFunc>
#include <osg/Material>
#include <osg/Program>
#include <osg/StateSet>
#include <osgUtil/StateGraph>
#include <unordered_set>

using namespace osgUtil;

namespace
{
    template <typename T>
    inline void accumulateState(T& currentValue, T newValue, bool& isOverride, unsigned int overrideFlags)
    {
        if (isOverride && !(overrideFlags & osg::StateAttribute::PROTECTED))
            return;

        if (overrideFlags & osg::StateAttribute::OVERRIDE)
            isOverride = true;

        currentValue = newValue;
    }

    inline void accumulateModeState(const osg::StateSet* ss, bool& currentValue, bool& isOverride, int mode)
    {
        const osg::StateSet::ModeList& l = ss->getModeList();
        osg::StateSet::ModeList::const_iterator mf = l.find(mode);
        if (mf == l.end())
            return;
        unsigned int flags = mf->second;
        bool newValue = flags & osg::StateAttribute::ON;
        accumulateState(currentValue, newValue, isOverride, flags);
    }

    inline bool materialNeedShadows(osg::Material* m)
    {
        // I'm pretty sure this needs to check the colour mode - vertex colours might override this value.
        return m->getDiffuse(osg::Material::FRONT).a() > 0.5;
    }
}

namespace SceneUtil
{

    ShadowsBin::ShadowsBin(const CastingPrograms& castingPrograms)
    {
        mNoTestStateSet = new osg::StateSet;
        mNoTestStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false));
        mNoTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", false));

        mShaderAlphaTestStateSet = new osg::StateSet;
        mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true));
        mShaderAlphaTestStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
        mShaderAlphaTestStateSet->setMode(
            GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);

        for (size_t i = 0; i < castingPrograms.size(); ++i)
        {
            mAlphaFuncShaders[i] = new osg::StateSet;
            mAlphaFuncShaders[i]->setAttribute(castingPrograms[i],
                osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);
        }
    }

    StateGraph* ShadowsBin::cullStateGraph(
        StateGraph* sg, StateGraph* root, std::unordered_set<StateGraph*>& uninterestingCache, bool cullFaceOverridden)
    {
        std::vector<StateGraph*> return_path;
        State state;
        StateGraph* sg_new = sg;
        do
        {
            if (uninterestingCache.find(sg_new) != uninterestingCache.end())
                break;
            return_path.push_back(sg_new);
            sg_new = sg_new->_parent;
        } while (sg_new && sg_new != root);

        for (auto itr = return_path.rbegin(); itr != return_path.rend(); ++itr)
        {
            const osg::StateSet* ss = (*itr)->getStateSet();
            if (!ss)
                continue;

            accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND);

            const osg::StateSet::AttributeList& attributes = ss->getAttributeList();
            osg::StateSet::AttributeList::const_iterator found
                = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0));
            if (found != attributes.end())
            {
                const osg::StateSet::RefAttributePair& rap = found->second;
                accumulateState(
                    state.mMaterial, static_cast<osg::Material*>(rap.first.get()), state.mMaterialOverride, rap.second);
                if (state.mMaterial && !materialNeedShadows(state.mMaterial))
                    state.mMaterial = nullptr;
            }

            found = attributes.find(std::make_pair(osg::StateAttribute::ALPHAFUNC, 0));
            if (found != attributes.end())
            {
                // As force shaders is on, we know this is really a RemovedAlphaFunc
                const osg::StateSet::RefAttributePair& rap = found->second;
                accumulateState(state.mAlphaFunc, static_cast<osg::AlphaFunc*>(rap.first.get()),
                    state.mAlphaFuncOverride, rap.second);
            }

            if (!cullFaceOverridden)
            {
                // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything
                // under it unless GL_CULL_FACE is off or we flip face culling.
                found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0));
                if (found != attributes.end())
                    state.mImportantState = true;
            }

            if ((*itr) != sg && !state.interesting())
                uninterestingCache.insert(*itr);
        }

        if (!state.needShadows())
            return nullptr;

        if (!state.needTexture() && !state.mImportantState)
        {
            for (RenderLeaf* leaf : sg->_leaves)
            {
                leaf->_parent = root;
                root->_leaves.push_back(leaf);
            }
            return nullptr;
        }

        if (state.mAlphaBlend)
        {
            sg_new = sg->find_or_insert(mShaderAlphaTestStateSet);
            sg_new->_leaves = std::move(sg->_leaves);
            for (RenderLeaf* leaf : sg_new->_leaves)
                leaf->_parent = sg_new;
            sg = sg_new;
        }

        // GL_ALWAYS is set by default by mwshadowtechnique
        if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS)
        {
            sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]);
            sg_new->_leaves = std::move(sg->_leaves);
            for (RenderLeaf* leaf : sg_new->_leaves)
                leaf->_parent = sg_new;
            sg = sg_new;
        }

        return sg;
    }

    inline bool ShadowsBin::State::needTexture() const
    {
        return mAlphaBlend || (mAlphaFunc && mAlphaFunc->getFunction() != GL_ALWAYS);
    }

    bool ShadowsBin::State::needShadows() const
    {
        if (mAlphaFunc && mAlphaFunc->getFunction() == GL_NEVER)
            return false;
        // other alpha func + material combinations might be skippable
        if (mAlphaBlend && mMaterial)
            return materialNeedShadows(mMaterial);
        return true;
    }

    void ShadowsBin::sortImplementation()
    {
        // The cull visitor contains a stategraph.
        // When a stateset is pushed, it's added/found as a child of the current stategraph node, then that node becomes
        // the new current stategraph node. When a drawable is added, the current stategraph node is added to the
        // current renderbin (if it's not there already) and the drawable is added as a renderleaf to the stategraph
        // This means our list only contains stategraph nodes with directly-attached renderleaves, but they might have
        // parents with more state set that needs to be considered.
        if (!_stateGraphList.size())
            return;
        StateGraph* root = _stateGraphList[0];
        while (root->_parent)
        {
            root = root->_parent;
            const osg::StateSet* ss = root->getStateSet();
            if (ss->getMode(GL_NORMALIZE) & osg::StateAttribute::ON // that is root stategraph of renderingmanager cpp
                || ss->getAttribute(osg::StateAttribute::VIEWPORT)) // fallback to rendertarget's sg just in case
                break;
            if (!root->_parent)
                return;
        }
        StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get());
        // noTestRoot is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state

        bool cullFaceOverridden = false;
        while (root->_parent)
        {
            root = root->_parent;
            if (!root->getStateSet())
                continue;
            unsigned int cullFaceFlags = root->getStateSet()->getMode(GL_CULL_FACE);
            if (cullFaceFlags & osg::StateAttribute::OVERRIDE && !(cullFaceFlags & osg::StateAttribute::ON))
            {
                cullFaceOverridden = true;
                break;
            }
        }

        noTestRoot->_leaves.reserve(_stateGraphList.size());
        StateGraphList newList;
        std::unordered_set<StateGraph*> uninterestingCache;
        for (StateGraph* graph : _stateGraphList)
        {
            // Render leaves which shouldn't use the diffuse map for shadow alpha but do cast shadows become children of
            // root, so graph is now empty. Don't add to newList. Graphs containing just render leaves which don't cast
            // shadows are discarded. Don't add to newList. Graphs containing other leaves need to be in newList.
            StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache, cullFaceOverridden);
            if (graphToAdd)
                newList.push_back(graphToAdd);
        }
        if (!noTestRoot->_leaves.empty())
            newList.push_back(noTestRoot);
        _stateGraphList = std::move(newList);
    }

}