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