1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 #include <osgShadow/ShadowTexture>
15 #include <osgShadow/ShadowedScene>
16 #include <osg/Notify>
17 #include <osg/ComputeBoundsVisitor>
18 #include <osg/io_utils>
19 
20 using namespace osgShadow;
21 
ShadowTexture()22 ShadowTexture::ShadowTexture():
23     _textureUnit(1)
24 {
25 }
26 
ShadowTexture(const ShadowTexture & copy,const osg::CopyOp & copyop)27 ShadowTexture::ShadowTexture(const ShadowTexture& copy, const osg::CopyOp& copyop):
28     ShadowTechnique(copy,copyop),
29     _textureUnit(copy._textureUnit)
30 {
31 }
32 
setTextureUnit(unsigned int unit)33 void ShadowTexture::setTextureUnit(unsigned int unit)
34 {
35     _textureUnit = unit;
36 }
37 
init()38 void ShadowTexture::init()
39 {
40     if (!_shadowedScene) return;
41 
42     unsigned int tex_width = 512;
43     unsigned int tex_height = 512;
44 
45     _texture = new osg::Texture2D;
46     _texture->setTextureSize(tex_width, tex_height);
47     _texture->setInternalFormat(GL_RGB);
48     _texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
49     _texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
50     _texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
51     _texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
52     _texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
53 
54     // set up the render to texture camera.
55     {
56         // create the camera
57         _camera = new osg::Camera;
58 
59         _camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
60 
61         _camera->setCullCallback(new CameraCullCallback(this));
62 
63         // set viewport
64         _camera->setViewport(0,0,tex_width,tex_height);
65 
66         // set the camera to render before the main camera.
67         _camera->setRenderOrder(osg::Camera::PRE_RENDER);
68 
69         // tell the camera to use OpenGL frame buffer object where supported.
70         _camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
71         //_camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
72 
73         // attach the texture and use it as the color buffer.
74         _camera->attach(osg::Camera::COLOR_BUFFER, _texture.get());
75 
76         _material = new osg::Material;
77         _material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
78         _material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
79         _material->setEmission(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
80         _material->setShininess(osg::Material::FRONT_AND_BACK,0.0f);
81 
82         osg::StateSet* stateset = _camera->getOrCreateStateSet();
83         stateset->setAttribute(_material.get(),osg::StateAttribute::OVERRIDE);
84 
85     }
86 
87     {
88         _stateset = new osg::StateSet;
89         _stateset->setTextureAttributeAndModes(_textureUnit,_texture.get(),osg::StateAttribute::ON);
90         _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
91         _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
92         _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
93         _stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
94 
95         _texgen = new osg::TexGen;
96     }
97 
98     _dirty = false;
99 }
100 
101 
update(osg::NodeVisitor & nv)102 void ShadowTexture::update(osg::NodeVisitor& nv)
103 {
104     _shadowedScene->osg::Group::traverse(nv);
105 }
106 
cull(osgUtil::CullVisitor & cv)107 void ShadowTexture::cull(osgUtil::CullVisitor& cv)
108 {
109     // record the traversal mask on entry so we can reapply it later.
110     unsigned int traversalMask = cv.getTraversalMask();
111 
112     osgUtil::RenderStage* orig_rs = cv.getRenderStage();
113 
114     // do traversal of shadow casting scene which does not need to be decorated by the shadow texture
115     {
116         cv.setTraversalMask( traversalMask &
117                              getShadowedScene()->getCastsShadowTraversalMask() );
118 
119         _shadowedScene->osg::Group::traverse(cv);
120     }
121 
122     // do traversal of shadow receiving scene which does need to be decorated by the shadow texture
123     {
124         cv.pushStateSet(_stateset.get());
125 
126         cv.setTraversalMask( traversalMask &
127                              getShadowedScene()->getReceivesShadowTraversalMask() );
128 
129         _shadowedScene->osg::Group::traverse(cv);
130 
131         cv.popStateSet();
132 
133     }
134 
135     // need to compute view frustum for RTT camera.
136     // 1) get the light position
137     // 2) get the center and extents of the view frustum
138 
139     const osg::Light* selectLight = 0;
140     osg::Vec4 lightpos;
141 
142     osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
143     for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
144         itr != aml.end();
145         ++itr)
146     {
147         const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
148         if (light)
149         {
150             osg::RefMatrix* matrix = itr->second.get();
151             if (matrix) lightpos = light->getPosition() * (*matrix);
152             else lightpos = light->getPosition();
153 
154             selectLight = light;
155         }
156     }
157 
158     osg::Matrix eyeToWorld;
159     eyeToWorld.invert(*cv.getModelViewMatrix());
160 
161     lightpos = lightpos * eyeToWorld;
162 
163     if (selectLight)
164     {
165 
166         // get the bounds of the model.
167         osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
168         cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
169 
170         _shadowedScene->osg::Group::traverse(cbbv);
171 
172         osg::BoundingBox bb = cbbv.getBoundingBox();
173 
174         if (lightpos[3]!=0.0)
175         {
176             osg::Vec3 position(lightpos.x(), lightpos.y(), lightpos.z());
177 
178             float centerDistance = (position-bb.center()).length();
179 
180             float znear = centerDistance-bb.radius();
181             float zfar  = centerDistance+bb.radius();
182             float zNearRatio = 0.001f;
183             if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
184 
185             float top   = (bb.radius()/centerDistance)*znear;
186             float right = top;
187 
188             _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
189             _camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
190             _camera->setViewMatrixAsLookAt(position,bb.center(),computeOrthogonalVector(bb.center()-position));
191 
192 
193             // compute the matrix which takes a vertex from local coords into tex coords
194             // will use this later to specify osg::TexGen..
195             osg::Matrix MVPT = _camera->getViewMatrix() *
196                                _camera->getProjectionMatrix() *
197                                osg::Matrix::translate(1.0,1.0,1.0) *
198                                osg::Matrix::scale(0.5f,0.5f,0.5f);
199 
200             _texgen->setMode(osg::TexGen::EYE_LINEAR);
201             _texgen->setPlanesFromMatrix(MVPT);
202         }
203         else
204         {
205             // make an orthographic projection
206             osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
207             lightDir.normalize();
208 
209             // set the position far away along the light direction
210             osg::Vec3 position = bb.center() + lightDir * bb.radius() * 2.0;
211 
212             float centerDistance = (position-bb.center()).length();
213 
214             float znear = centerDistance-bb.radius();
215             float zfar  = centerDistance+bb.radius();
216             float zNearRatio = 0.001f;
217             if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
218 
219             float top   = bb.radius();
220             float right = top;
221 
222             _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
223             _camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
224             _camera->setViewMatrixAsLookAt(position,bb.center(),computeOrthogonalVector(lightDir));
225 
226 
227             // compute the matrix which takes a vertex from local coords into tex coords
228             // will use this later to specify osg::TexGen..
229             osg::Matrix MVPT = _camera->getViewMatrix() *
230                                _camera->getProjectionMatrix() *
231                                osg::Matrix::translate(1.0,1.0,1.0) *
232                                osg::Matrix::scale(0.5f,0.5f,0.5f);
233 
234             _texgen->setMode(osg::TexGen::EYE_LINEAR);
235             _texgen->setPlanesFromMatrix(MVPT);
236         }
237 
238 
239         cv.setTraversalMask( traversalMask &
240                              getShadowedScene()->getCastsShadowTraversalMask() );
241 
242         // do RTT camera traversal
243         _camera->accept(cv);
244 
245         orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_textureUnit, cv.getModelViewMatrix(), _texgen.get());
246     }
247 
248 
249 
250 
251 
252     // reapply the original traversal mask
253     cv.setTraversalMask( traversalMask );
254 }
255 
cleanSceneGraph()256 void ShadowTexture::cleanSceneGraph()
257 {
258 }
259