diff --git a/simgear/scene/viewer/ClusteredForward.cxx b/simgear/scene/viewer/ClusteredForward.cxx index 6e5f4098..e2c9d593 100644 --- a/simgear/scene/viewer/ClusteredForward.cxx +++ b/simgear/scene/viewer/ClusteredForward.cxx @@ -49,9 +49,9 @@ Light LIGHT_LIST[NUM_LIGHTS] = { }; ///// END DEBUG -ClusteredForwardDrawCallback::ClusteredForwardDrawCallback() : +ClusteredForwardDrawCallback::ClusteredForwardDrawCallback(int tile_size) : _initialized(false), - _tile_size(64), + _tile_size(tile_size), _light_grid(new osg::Image), _light_indices(new osg::Image), _light_data(new osg::FloatArray(MAX_POINT_LIGHTS)) @@ -105,15 +105,15 @@ ClusteredForwardDrawCallback::operator()(osg::RenderInfo &renderInfo) const _light_data->setBufferObject(light_data_ubo.get()); #if OSG_VERSION_LESS_THAN(3,6,0) - osg::ref_ptr light_data_ubb = + osg::ref_ptr light_data_ubb = new osg::UniformBufferBinding(0, light_data_ubo.get(), 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); #else - osg::ref_ptr light_data_ubb = + osg::ref_ptr light_data_ubb = new osg::UniformBufferBinding(0, _light_data.get(), 0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat)); #endif -light_data_ubb->setDataVariance(osg::Object::DYNAMIC); + light_data_ubb->setDataVariance(osg::Object::DYNAMIC); camera->getOrCreateStateSet()->setAttribute( light_data_ubb.get(), osg::StateAttribute::ON); diff --git a/simgear/scene/viewer/ClusteredForward.hxx b/simgear/scene/viewer/ClusteredForward.hxx index 8afa47b0..63da3af6 100644 --- a/simgear/scene/viewer/ClusteredForward.hxx +++ b/simgear/scene/viewer/ClusteredForward.hxx @@ -24,7 +24,7 @@ namespace compositor { class ClusteredForwardDrawCallback : public osg::Camera::DrawCallback { public: - ClusteredForwardDrawCallback(); + ClusteredForwardDrawCallback(int tile_size); virtual void operator()(osg::RenderInfo &renderInfo) const; protected: mutable bool _initialized; diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index f1caf50c..a86385b6 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -39,7 +39,8 @@ Compositor * Compositor::create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const SGPropertyNode *property_list) + const SGPropertyNode *property_list, + const SGReaderWriterOptions *options) { osg::ref_ptr compositor = new Compositor(view, gc, viewport); compositor->_name = property_list->getStringValue("name"); @@ -55,7 +56,7 @@ Compositor::create(osg::View *view, "a name to be available to passes. Skipping..."); continue; } - Buffer *buffer = buildBuffer(compositor.get(), p_buffer); + Buffer *buffer = buildBuffer(compositor.get(), p_buffer, options); if (buffer) compositor->addBuffer(buffer_name, buffer); } @@ -64,7 +65,7 @@ Compositor::create(osg::View *view, for (auto const &p_pass : p_passes) { if (!checkConditional(p_pass)) continue; - Pass *pass = buildPass(compositor.get(), p_pass); + Pass *pass = buildPass(compositor.get(), p_pass, options); if (pass) compositor->addPass(pass); } @@ -76,7 +77,8 @@ Compositor * Compositor::create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const std::string &name) + const std::string &name, + const SGReaderWriterOptions *options) { std::string filename(name); filename += ".xml"; @@ -96,7 +98,7 @@ Compositor::create(osg::View *view, return 0; } - return create(view, gc, viewport, property_list); + return create(view, gc, viewport, property_list, options); } Compositor::Compositor(osg::View *view, diff --git a/simgear/scene/viewer/Compositor.hxx b/simgear/scene/viewer/Compositor.hxx index c1761ef1..63be4e8b 100644 --- a/simgear/scene/viewer/Compositor.hxx +++ b/simgear/scene/viewer/Compositor.hxx @@ -73,7 +73,8 @@ public: static Compositor *create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const SGPropertyNode *property_list); + const SGPropertyNode *property_list, + const SGReaderWriterOptions *options); /** * \overload * \brief Create a Compositor from a file. @@ -84,7 +85,8 @@ public: static Compositor *create(osg::View *view, osg::GraphicsContext *gc, osg::Viewport *viewport, - const std::string &name); + const std::string &name, + const SGReaderWriterOptions *options); void update(const osg::Matrix &view_matrix, const osg::Matrix &proj_matrix); @@ -95,9 +97,11 @@ public: const osg::Vec2d& windowPos, osgUtil::LineSegmentIntersector::Intersections& intersections); - const osg::GraphicsContext *getGraphicsContext() const { return _gc; } + osg::View *getView() const { return _view; } - const osg::Viewport *getViewport() const { return _viewport; } + osg::GraphicsContext *getGraphicsContext() const { return _gc; } + + osg::Viewport *getViewport() const { return _viewport; } typedef std::array< osg::ref_ptr, @@ -121,8 +125,6 @@ public: Pass * getPass(const std::string &name) const; protected: - friend class PassBuilder; - osg::View *_view; osg::GraphicsContext *_gc; osg::ref_ptr _viewport; diff --git a/simgear/scene/viewer/CompositorBuffer.cxx b/simgear/scene/viewer/CompositorBuffer.cxx index 2acb6be9..80071ef4 100644 --- a/simgear/scene/viewer/CompositorBuffer.cxx +++ b/simgear/scene/viewer/CompositorBuffer.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include "Compositor.hxx" #include "CompositorUtil.hxx" @@ -49,6 +50,7 @@ PropStringMap buffer_format_map { {"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}}, {"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}}, {"r32f", {GL_R32F, GL_RED, GL_FLOAT}}, + {"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}}, {"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}}, {"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}}, {"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}}, @@ -92,7 +94,8 @@ PropStringMap shadow_compare_func_map = { }; Buffer * -buildBuffer(Compositor *compositor, const SGPropertyNode *node) +buildBuffer(Compositor *compositor, const SGPropertyNode *node, + const SGReaderWriterOptions *options) { std::string type = node->getStringValue("type"); if (type.empty()) { diff --git a/simgear/scene/viewer/CompositorBuffer.hxx b/simgear/scene/viewer/CompositorBuffer.hxx index df0a92a4..92a9d5e0 100644 --- a/simgear/scene/viewer/CompositorBuffer.hxx +++ b/simgear/scene/viewer/CompositorBuffer.hxx @@ -22,6 +22,9 @@ class SGPropertyNode; namespace simgear { + +class SGReaderWriterOptions; + namespace compositor { class Compositor; @@ -38,7 +41,8 @@ struct Buffer : public osg::Referenced { float width_scale, height_scale; }; -Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node); +Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node, + const SGReaderWriterOptions *options); } // namespace compositor } // namespace simgear diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 8ee399d7..df28dfdf 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -22,9 +22,12 @@ #include #include +#include + #include #include #include +#include #include #include @@ -50,34 +53,40 @@ PropStringMap buffer_component_map = { {"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER} }; -Pass * -PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) -{ - // The pass index matches its render order - int render_order = root->getIndex(); +Pass * +PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) +{ osg::ref_ptr pass = new Pass; + // The pass index matches its render order + pass->render_order = root->getIndex(); pass->name = root->getStringValue("name"); if (pass->name.empty()) { - SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << render_order + SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << pass->render_order << " has no name. It won't be addressable by name!"); } pass->type = root->getStringValue("type"); std::string eff_override_file = root->getStringValue("effect-override"); if (!eff_override_file.empty()) - pass->effect_override = makeEffect(eff_override_file, true, 0); + pass->effect_override = makeEffect(eff_override_file, true, options); osg::Camera *camera = new Camera; pass->camera = camera; camera->setName(pass->name); - camera->setGraphicsContext(compositor->_gc); + camera->setGraphicsContext(compositor->getGraphicsContext()); // Even though this camera will be added as a slave to the view, it will // always be updated manually in Compositor::update() camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // Same with the projection matrix camera->setProjectionResizePolicy(osg::Camera::FIXED); + // We only use POST_RENDER. Leave PRE_RENDER for Canvas and other RTT stuff + // that doesn't involve the rendering pipeline itself. NESTED_RENDER is also + // not a possibility since we don't want to share RenderStage with the View + // master camera. + camera->setRenderOrder(osg::Camera::POST_RENDER, pass->render_order * 10); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); // XXX: Should we make this configurable? @@ -144,7 +153,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); } catch (sg_exception &e) { SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding " - << p_binding->getIndex() << " in pass " << render_order + << p_binding->getIndex() << " in pass " << pass->render_order << ": " << e.what()); } } @@ -153,22 +162,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) if (p_attachments.empty()) { // If there are no attachments, assume the pass is rendering // directly to the screen - - camera->setRenderOrder(osg::Camera::NESTED_RENDER, render_order * 10); - // OSG cameras use the framebuffer by default, but it is stated - // explicitly anyway camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER); - camera->setDrawBuffer(GL_BACK); camera->setReadBuffer(GL_BACK); // Use the physical viewport. We can't let the user choose the viewport // size because some parts of the window might not be ours. - camera->setViewport(compositor->_viewport); + camera->setViewport(compositor->getViewport()); } else { // This is a RTT camera - - camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); bool viewport_absolute = false; @@ -252,13 +254,13 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) camera->setViewport( 0, 0, - buffer->width_scale * compositor->_viewport->width(), - buffer->height_scale * compositor->_viewport->height()); + buffer->width_scale * compositor->getViewport()->width(), + buffer->height_scale * compositor->getViewport()->height()); } } } catch (sg_exception &e) { SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment " - << p_attachment->getIndex() << " in pass " << render_order + << p_attachment->getIndex() << " in pass " << pass->render_order << ": " << e.what()); } } @@ -271,8 +273,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root) struct QuadPassBuilder : public PassBuilder { public: - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); + pass->useMastersSceneData = false; osg::Camera *camera = pass->camera; camera->setAllowEventFocus(false); @@ -289,11 +293,29 @@ public: scale = p_geometry->getFloatValue("scale", scale); } - const std::string eff_file = root->getStringValue("effect"); - - osg::ref_ptr quad = createFullscreenQuad( - left, bottom, width, height, scale, eff_file); + osg::ref_ptr quad = new EffectGeode; camera->addChild(quad); + quad->setCullingActive(false); + + const std::string eff_file = root->getStringValue("effect"); + if (!eff_file.empty()) { + Effect *eff = makeEffect(eff_file, true, options); + if (eff) + quad->setEffect(eff); + } + + osg::ref_ptr geom = createFullscreenQuadGeom( + left, bottom, width, height, scale); + quad->addDrawable(geom); + + osg::ref_ptr quad_state = quad->getOrCreateStateSet(); + int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED; + quad_state->setAttribute(new osg::PolygonMode( + osg::PolygonMode::FRONT_AND_BACK, + osg::PolygonMode::FILL), + values); + quad_state->setMode(GL_LIGHTING, values); + quad_state->setMode(GL_DEPTH_TEST, values); osg::StateSet *ss = camera->getOrCreateStateSet(); for (const auto &uniform : compositor->getUniforms()) @@ -302,13 +324,12 @@ public: return pass.release(); } protected: - osg::Geode *createFullscreenQuad(float left, - float bottom, - float width, - float height, - float scale, - const std::string &eff_file) { - osg::Geometry *geom; + osg::Geometry *createFullscreenQuadGeom(float left, + float bottom, + float width, + float height, + float scale) { + osg::ref_ptr geom; // When the quad is fullscreen, it can be optimized by using a // a fullscreen triangle instead of a quad to avoid discarding pixels @@ -349,31 +370,52 @@ protected: osg::PrimitiveSet::TRIANGLES, 0, 3)); } - osg::ref_ptr quad = new EffectGeode; - if (!eff_file.empty()) { - Effect *eff = makeEffect(eff_file, true, 0); - if (eff) - quad->setEffect(eff); - } - quad->addDrawable(geom); - quad->setCullingActive(false); - - osg::ref_ptr quad_state = quad->getOrCreateStateSet(); - int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED; - quad_state->setAttribute(new osg::PolygonMode( - osg::PolygonMode::FRONT_AND_BACK, - osg::PolygonMode::FILL), - values); - quad_state->setMode(GL_LIGHTING, values); - quad_state->setMode(GL_DEPTH_TEST, values); - - return quad.release(); + return geom.release(); } }; RegisterPassBuilder registerQuadPass("quad"); //------------------------------------------------------------------------------ +class ShadowMapCullCallback : public osg::NodeCallback { +public: + ShadowMapCullCallback(const std::string &suffix) { + _light_matrix_uniform = new osg::Uniform( + osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); + } + + virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { + osg::Camera *camera = static_cast(node); + + traverse(node, nv); + + // The light matrix uniform is updated after the traverse in case the + // OSG near/far plane calculations were enabled + osg::Matrixf light_matrix = + // Include the real camera inverse view matrix because if the shader + // used world coordinates, there would be precision issues. + _real_inverse_view * + camera->getViewMatrix() * + camera->getProjectionMatrix() * + // Bias matrices + osg::Matrix::translate(1.0, 1.0, 1.0) * + osg::Matrix::scale(0.5, 0.5, 0.5); + _light_matrix_uniform->set(light_matrix); + } + + void setRealInverseViewMatrix(const osg::Matrix &matrix) { + _real_inverse_view = matrix; + } + + osg::Uniform *getLightMatrixUniform() const { + return _light_matrix_uniform.get(); + } + +protected: + osg::Matrix _real_inverse_view; + osg::ref_ptr _light_matrix_uniform; +}; + class LightFinder : public osg::NodeVisitor { public: LightFinder(const std::string &name) : @@ -406,15 +448,14 @@ protected: struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback { public: - ShadowMapUpdateCallback(const std::string &light_name, + ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback, + const std::string &light_name, float near_m, float far_m, - const std::string &suffix, int sm_width, int sm_height) : + _cull_callback(cull_callback), _light_finder(new LightFinder(light_name)), _near_m(near_m), _far_m(far_m) { - _light_matrix_uniform = new osg::Uniform( - osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); _half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0; } virtual void updatePass(Pass &pass, @@ -443,6 +484,7 @@ public: // This is not a problem though (for now). osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix); + _cull_callback->setRealInverseViewMatrix(view_inverse); // Calculate the light's point of view transformation matrices. // Taken from Project Rembrandt. @@ -489,37 +531,30 @@ public: osg::Matrixd round_matrix = osg::Matrixd::translate( rounding.x(), rounding.y(), 0.0); light_proj_matrix *= round_matrix; - - osg::Matrixf light_matrix = - // Include the real camera inverse view matrix because if the shader - // used world coordinates, there would be precision issues. - view_inverse * - camera->getViewMatrix() * - camera->getProjectionMatrix() * - // Bias matrices - osg::Matrix::translate(1.0, 1.0, 1.0) * - osg::Matrix::scale(0.5, 0.5, 0.5); - _light_matrix_uniform->set(light_matrix); } - osg::Uniform *getLightMatrixUniform() const { - return _light_matrix_uniform.get(); - } protected: + osg::observer_ptr _cull_callback; osg::ref_ptr _light_finder; float _near_m; float _far_m; - osg::ref_ptr _light_matrix_uniform; osg::Vec2d _half_sm_size; }; struct ShadowMapPassBuilder : public PassBuilder { - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); - pass->useMastersSceneData = true; + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); osg::Camera *camera = pass->camera; camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); + camera->setCullingMode(camera->getCullingMode() & + ~osg::CullSettings::SMALL_FEATURE_CULLING); + //camera->setComputeNearFarMode( + // osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); + + ShadowMapCullCallback *cull_callback = new ShadowMapCullCallback(pass->name); + camera->setCullCallback(cull_callback); std::string light_name = root->getStringValue("light-name"); float near_m = root->getFloatValue("near-m"); @@ -527,9 +562,9 @@ struct ShadowMapPassBuilder : public PassBuilder { int sm_width = camera->getViewport()->width(); int sm_height = camera->getViewport()->height(); pass->update_callback = new ShadowMapUpdateCallback( + cull_callback, light_name, near_m, far_m, - pass->name, sm_width, sm_height); return pass.release(); @@ -626,9 +661,9 @@ protected: struct ScenePassBuilder : public PassBuilder { public: - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) { - osg::ref_ptr pass = PassBuilder::build(compositor, root); - pass->useMastersSceneData = true; + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { + osg::ref_ptr pass = PassBuilder::build(compositor, root, options); pass->inherit_cull_mask = true; osg::Camera *camera = pass->camera; @@ -636,7 +671,8 @@ public: const SGPropertyNode *clustered = root->getChild("clustered-forward"); if (clustered) { - camera->setInitialDrawCallback(new ClusteredForwardDrawCallback); + int tile_size = clustered->getIntValue("tile-size", 64); + camera->setInitialDrawCallback(new ClusteredForwardDrawCallback(tile_size)); } int cubemap_face = root->getIntValue("cubemap-face", -1); @@ -644,23 +680,26 @@ public: float zFar = root->getFloatValue("z-far", 0.0f); pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar); - std::string shadow_pass_name = root->getStringValue("use-shadow-pass"); - if (!shadow_pass_name.empty()) { - Pass *shadow_pass = compositor->getPass(shadow_pass_name); - if (shadow_pass) { - ShadowMapUpdateCallback *updatecb = - dynamic_cast( - shadow_pass->update_callback.get()); - if (updatecb) { - camera->getOrCreateStateSet()->addUniform( - updatecb->getLightMatrixUniform()); + PropertyList p_shadow_passes = root->getChildren("use-shadow-pass"); + for (const auto &p_shadow_pass : p_shadow_passes) { + std::string shadow_pass_name = p_shadow_pass->getStringValue(); + if (!shadow_pass_name.empty()) { + Pass *shadow_pass = compositor->getPass(shadow_pass_name); + if (shadow_pass) { + ShadowMapCullCallback *cullcb = + dynamic_cast( + shadow_pass->camera->getCullCallback()); + if (cullcb) { + camera->getOrCreateStateSet()->addUniform( + cullcb->getLightMatrixUniform()); + } else { + SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" + << shadow_pass_name << "is not a shadow pass"); + } } else { - SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" - << shadow_pass_name << "is not a shadow pass"); + SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not " + "find shadow pass named '" << shadow_pass_name << "'"); } - } else { - SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not " - "find shadow pass named '" << shadow_pass_name << "'"); } } @@ -673,7 +712,8 @@ RegisterPassBuilder registerScenePass("scene"); //------------------------------------------------------------------------------ Pass * -buildPass(Compositor *compositor, const SGPropertyNode *root) +buildPass(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options) { std::string type = root->getStringValue("type"); if (type.empty()) { @@ -687,7 +727,7 @@ buildPass(Compositor *compositor, const SGPropertyNode *root) return 0; } - return builder->build(compositor, root); + return builder->build(compositor, root, options); } } // namespace compositor diff --git a/simgear/scene/viewer/CompositorPass.hxx b/simgear/scene/viewer/CompositorPass.hxx index 68fe89e3..09fe86f1 100644 --- a/simgear/scene/viewer/CompositorPass.hxx +++ b/simgear/scene/viewer/CompositorPass.hxx @@ -27,6 +27,9 @@ #include namespace simgear { + +class SGReaderWriterOptions; + namespace compositor { class Compositor; @@ -45,12 +48,13 @@ class Compositor; */ struct Pass : public osg::Referenced { Pass() : - useMastersSceneData(false), + useMastersSceneData(true), cull_mask(0xffffff), inherit_cull_mask(false), viewport_width_scale(0.0f), viewport_height_scale(0.0f) {} + int render_order; std::string name; std::string type; osg::ref_ptr camera; @@ -85,10 +89,11 @@ public: * and overrided for more special passes. * * @param compositor The Compositor instance that owns the pass. - * @param The root node of the pass property tree. + * @param root The root node of the pass property tree. * @return A Pass or a null pointer if an error occurred. */ - virtual Pass *build(Compositor *compositor, const SGPropertyNode *root); + virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options); static PassBuilder *find(const std::string &type) { auto itr = PassBuilderMapSingleton::instance()->_map.find(type); @@ -125,7 +130,8 @@ struct RegisterPassBuilder { * @param node The root node of the pass property tree. * @return A Pass or a null pointer if an error occurred. */ -Pass *buildPass(Compositor *compositor, const SGPropertyNode *root); +Pass *buildPass(Compositor *compositor, const SGPropertyNode *root, + const SGReaderWriterOptions *options); } // namespace compositor } // namespace simgear