]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/Technique.cxx
4258b46422e6c825d27261b7a6b5f7f57e30d5b9
[simgear.git] / simgear / scene / material / Technique.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <simgear_config.h>
4 #endif
5
6 #include "Technique.hxx"
7 #include "Pass.hxx"
8
9 #include <boost/foreach.hpp>
10
11 #include <iterator>
12 #include <vector>
13 #include <string>
14
15 #include <osg/GLExtensions>
16 #include <osg/Math>
17 #include <osgUtil/CullVisitor>
18
19 #include <osgDB/Registry>
20 #include <osgDB/Input>
21 #include <osgDB/ParameterOutput>
22
23 #include <simgear/props/props.hxx>
24 #include <simgear/structure/OSGUtils.hxx>
25
26 namespace simgear
27 {
28 using namespace osg;
29 using namespace osgUtil;
30
31 namespace
32 {
33
34 struct ValidateOperation : GraphicsOperation
35 {
36     ValidateOperation(Technique* technique_)
37         : GraphicsOperation(opName, false), technique(technique_)
38     {
39     }
40     virtual void operator() (GraphicsContext* gc);
41     osg::ref_ptr<Technique> technique;
42     static const std::string opName;
43 };
44
45 const std::string ValidateOperation::opName("ValidateOperation");
46
47
48 void ValidateOperation::operator() (GraphicsContext* gc)
49 {
50     technique->validateInContext(gc);
51 }
52 }
53
54 Technique::Technique(bool alwaysValid)
55     : _alwaysValid(alwaysValid), _contextIdLocation(-1)
56 {
57 }
58
59 Technique::Technique(const Technique& rhs, const osg::CopyOp& copyop) :
60     _contextMap(rhs._contextMap), _alwaysValid(rhs._alwaysValid),
61     _shadowingStateSet(copyop(rhs._shadowingStateSet)),
62     _validExpression(rhs._validExpression),
63     _contextIdLocation(rhs._contextIdLocation)
64 {
65     for (std::vector<ref_ptr<Pass> >::const_iterator itr = rhs.passes.begin(),
66              end = rhs.passes.end();
67          itr != end;
68          ++itr)
69         passes.push_back(static_cast<Pass*>(copyop(itr->get())));
70 }
71
72 Technique::~Technique()
73 {
74 }
75
76 Technique::Status Technique::valid(osg::RenderInfo* renderInfo)
77 {
78     if (_alwaysValid)
79         return VALID;
80     unsigned contextID = renderInfo->getContextID();
81     ContextInfo& contextInfo = _contextMap[contextID];
82     Status status = contextInfo.valid();
83     if (status != UNKNOWN)
84         return status;
85     Status newStatus = QUERY_IN_PROGRESS;
86     // lock and spawn validity check.
87     if (!contextInfo.valid.compareAndSwap(status, newStatus)) {
88         // Lost the race with another thread spawning a request
89         return contextInfo.valid();
90     }
91     ref_ptr<ValidateOperation> validOp = new ValidateOperation(this);
92     GraphicsContext* context = renderInfo->getState()->getGraphicsContext();
93     GraphicsThread* thread = context->getGraphicsThread();
94     if (thread)
95         thread->add(validOp.get());
96     else
97         context->add(validOp.get());
98     return newStatus;
99 }
100
101 Technique::Status Technique::getValidStatus(const RenderInfo* renderInfo) const
102 {
103     if (_alwaysValid)
104         return VALID;
105     ContextInfo& contextInfo = _contextMap[renderInfo->getContextID()];
106     return contextInfo.valid();
107 }
108
109 void Technique::validateInContext(GraphicsContext* gc)
110 {
111     unsigned int contextId = gc->getState()->getContextID();
112     ContextInfo& contextInfo = _contextMap[contextId];
113     Status oldVal = contextInfo.valid();
114     Status newVal = INVALID;
115     expression::FixedLengthBinding<1> binding;
116     binding.getBindings()[_contextIdLocation].val.intVal = contextId;
117     if (_validExpression->getValue(&binding))
118         newVal = VALID;
119     contextInfo.valid.compareAndSwap(oldVal, newVal);
120 }
121
122 namespace
123 {
124 enum NumDrawables {NUM_DRAWABLES = 128};
125 }
126
127 EffectGeode::DrawablesIterator
128 Technique::processDrawables(const EffectGeode::DrawablesIterator& begin,
129                             const EffectGeode::DrawablesIterator& end,
130                             CullVisitor* cv,
131                             bool isCullingActive)
132 {
133     RefMatrix& matrix = *cv->getModelViewMatrix();
134     float depth[NUM_DRAWABLES];
135     EffectGeode::DrawablesIterator itr = begin;
136     bool computeNearFar
137         = cv->getComputeNearFarMode() != CullVisitor::DO_NOT_COMPUTE_NEAR_FAR;
138     for (int i = 0; i < NUM_DRAWABLES && itr != end; ++itr, ++i) {
139         Drawable* drawable = itr->get();
140         const BoundingBox& bb = drawable->getBound();
141         if ((drawable->getCullCallback()
142              && drawable->getCullCallback()->cull(cv, drawable,
143                                                   &cv->getRenderInfo()))
144             || (isCullingActive && cv->isCulled(bb))) {
145             depth[i] = FLT_MAX;
146             continue;
147         }
148         if (computeNearFar && bb.valid()) {
149             if (!cv->updateCalculatedNearFar(matrix, *drawable, false)) {
150                 depth[i] = FLT_MAX;
151                 continue;
152             }
153         }
154         depth[i] = (bb.valid()
155                     ? cv->getDistanceFromEyePoint(bb.center(), false)
156                     : 0.0f);
157         if (isNaN(depth[i]))
158             depth[i] = FLT_MAX;
159     }
160     EffectGeode::DrawablesIterator drawablesEnd = itr;
161     BOOST_FOREACH(ref_ptr<Pass>& pass, passes)
162     {
163         cv->pushStateSet(pass.get());
164         int i = 0;
165         for (itr = begin; itr != drawablesEnd; ++itr, ++i) {
166             if (depth[i] != FLT_MAX)
167                 cv->addDrawableAndDepth(itr->get(), &matrix, depth[i]);
168         }
169         cv->popStateSet();
170     }
171     return drawablesEnd;
172 }
173
174 void Technique::resizeGLObjectBuffers(unsigned int maxSize)
175 {
176     if (_shadowingStateSet.valid())
177         _shadowingStateSet->resizeGLObjectBuffers(maxSize);
178     BOOST_FOREACH(ref_ptr<Pass>& pass, passes) {
179         pass->resizeGLObjectBuffers(maxSize);
180     }
181     _contextMap.resize(maxSize);
182 }
183
184 void Technique::releaseGLObjects(osg::State* state) const
185 {
186     if (_shadowingStateSet.valid())
187         _shadowingStateSet->releaseGLObjects(state);
188     BOOST_FOREACH(const ref_ptr<Pass>& pass, passes)
189     {
190         pass->releaseGLObjects(state);
191     }
192     if (state == 0) {
193         for (int i = 0; i < (int)_contextMap.size(); ++i) {
194             ContextInfo& info = _contextMap[i];
195             Status oldVal = info.valid();
196             info.valid.compareAndSwap(oldVal, UNKNOWN);
197         }
198     } else {
199         ContextInfo& info = _contextMap[state->getContextID()];
200         Status oldVal = info.valid();
201         info.valid.compareAndSwap(oldVal, UNKNOWN);
202     }
203 }
204
205 void Technique::setValidExpression(SGExpressionb* exp,
206                                    const simgear::expression
207                                    ::BindingLayout& layout)
208 {
209     using namespace simgear::expression;
210     _validExpression = exp;
211     VariableBinding binding;
212     if (layout.findBinding("__contextId", binding))
213         _contextIdLocation = binding.location;
214 }
215
216 class GLVersionExpression : public SGExpression<float>
217 {
218 public:
219     void eval(float& value, const expression::Binding*) const
220     {
221 #ifdef TECHNIQUE_TEST_EXTENSIONS
222         value = 1.1;
223 #else
224         value = getGLVersionNumber();
225 #endif
226     }
227 };
228
229 Expression* glVersionParser(const SGPropertyNode* exp,
230                             expression::Parser* parser)
231 {
232     return new GLVersionExpression();
233 }
234
235 expression::ExpParserRegistrar glVersionRegistrar("glversion", glVersionParser);
236
237 class ExtensionSupportedExpression
238     : public GeneralNaryExpression<bool, int>
239 {
240 public:
241     ExtensionSupportedExpression() {}
242     ExtensionSupportedExpression(const string& extString)
243         : _extString(extString)
244     {
245     }
246     const string& getExtensionString() { return _extString; }
247     void setExtensionString(const string& extString) { _extString = extString; }
248     void eval(bool&value, const expression::Binding* b) const
249     {
250         int contextId = getOperand(0)->getValue(b);
251         value = isGLExtensionSupported((unsigned)contextId, _extString.c_str());
252     }
253 protected:
254     string _extString;
255 };
256
257 Expression* extensionSupportedParser(const SGPropertyNode* exp,
258                                      expression::Parser* parser)
259 {
260     if (exp->getType() == props::STRING
261         || exp->getType() == props::UNSPECIFIED) {
262         ExtensionSupportedExpression* esp
263             = new ExtensionSupportedExpression(exp->getStringValue());
264         int location = parser->getBindingLayout().addBinding("__contextId",
265                                                              expression::INT);
266         VariableExpression<int>* contextExp
267             = new VariableExpression<int>(location);
268         esp->addOperand(contextExp);
269         return esp;
270     }
271     throw expression::ParseError("extension-supported expression has wrong type");
272 }
273
274 expression::ExpParserRegistrar
275 extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
276
277 void Technique::setGLExtensionsPred(float glVersion,
278                                     const std::vector<std::string>& extensions)
279 {
280     using namespace std;
281     using namespace expression;
282     BindingLayout layout;
283     int contextLoc = layout.addBinding("__contextId", INT);
284     VariableExpression<int>* contextExp
285         = new VariableExpression<int>(contextLoc);
286     SGExpression<bool>* versionTest
287         = makePredicate<std::less_equal>(new SGConstExpression<float>(glVersion),
288                         new GLVersionExpression);
289     AndExpression* extensionsExp = 0;
290     for (vector<string>::const_iterator itr = extensions.begin(),
291              e = extensions.end();
292          itr != e;
293          ++itr) {
294         if (!extensionsExp)
295             extensionsExp = new AndExpression;
296         ExtensionSupportedExpression* supported
297             = new ExtensionSupportedExpression(*itr);
298         supported->addOperand(contextExp);
299         extensionsExp->addOperand(supported);
300     }
301     SGExpressionb* predicate = 0;
302     if (extensionsExp) {
303         OrExpression* orExp = new OrExpression;
304         orExp->addOperand(versionTest);
305         orExp->addOperand(extensionsExp);
306         predicate = orExp;
307     } else {
308         predicate = versionTest;
309     }
310     setValidExpression(predicate, layout);
311 }
312
313 void Technique::refreshValidity()
314 {
315     for (int i = 0; i < (int)_contextMap.size(); ++i) {
316         ContextInfo& info = _contextMap[i];
317         Status oldVal = info.valid();
318         // What happens if we lose the race here?
319         info.valid.compareAndSwap(oldVal, UNKNOWN);
320     }
321 }
322
323 bool Technique_writeLocalData(const Object& obj, osgDB::Output& fw)
324 {
325     const Technique& tniq = static_cast<const Technique&>(obj);
326     fw.indent() << "alwaysValid "
327                 << (tniq.getAlwaysValid() ? "TRUE\n" : "FALSE\n");
328 #if 0
329     fw.indent() << "glVersion " << tniq.getGLVersion() << "\n";
330 #endif
331     if (tniq.getShadowingStateSet()) {
332         fw.indent() << "shadowingStateSet\n";
333         fw.writeObject(*tniq.getShadowingStateSet());
334     }
335     fw.indent() << "num_passes " << tniq.passes.size() << "\n";
336     BOOST_FOREACH(const ref_ptr<Pass>& pass, tniq.passes) {
337         fw.writeObject(*pass);
338     }
339     return true;
340 }
341
342 namespace
343 {
344 osgDB::RegisterDotOsgWrapperProxy TechniqueProxy
345 (
346     new Technique,
347     "simgear::Technique",
348     "Object simgear::Technique",
349     0,
350     &Technique_writeLocalData
351     );
352 }
353 }