root/OpenSceneGraph/trunk/examples/osgmovie/osgmovie.cpp @ 4956

Revision 4956, 17.0 kB (checked in by robert, 9 years ago)

Added support for tracking mouse movement and computing the intersection of the mouse position
into texture coords.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// -*-c++-*-
2
3#include <osgProducer/Viewer>
4
5#include <osgDB/ReadFile>
6
7#include <osg/Geode>
8#include <osg/Geometry>
9#include <osg/StateSet>
10#include <osg/Material>
11#include <osg/Texture2D>
12#include <osg/TextureRectangle>
13#include <osg/TexMat>
14#include <osg/CullFace>
15#include <osg/ImageStream>
16#include <osg/io_utils>
17
18#include <osgGA/TrackballManipulator>
19#include <osgGA/EventVisitor>
20
21osg::ImageStream* s_imageStream = 0;
22class PostSwapFinishCallback : public Producer::Camera::Callback
23{
24public:
25
26    PostSwapFinishCallback() {}
27
28    virtual void operator()(const Producer::Camera& camera)
29    {
30        // osg::Timer_t start_tick = osg::Timer::instance()->tick();
31       
32        osgProducer::OsgSceneHandler* sh = const_cast<osgProducer::OsgSceneHandler*>(dynamic_cast<const osgProducer::OsgSceneHandler*>(camera.getSceneHandler()));
33   
34        if (s_imageStream && s_imageStream->getPixelBufferObject()) s_imageStream->getPixelBufferObject()->compileBuffer(*(sh->getSceneView()->getState()));
35        // glFinish();
36
37        //osg::notify(osg::NOTICE)<<"callback after PBO "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
38    }
39};
40
41class MovieEventHandler : public osgGA::GUIEventHandler
42{
43public:
44
45    MovieEventHandler() {}
46   
47    void set(osg::Node* node);
48
49    virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }
50
51    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv);
52   
53    virtual void getUsage(osg::ApplicationUsage& usage) const;
54
55    typedef std::vector< osg::ref_ptr<osg::ImageStream> > ImageStreamList;
56
57protected:
58
59    virtual ~MovieEventHandler() {}
60
61    class FindImageStreamsVisitor : public osg::NodeVisitor
62    {
63    public:
64        FindImageStreamsVisitor(ImageStreamList& imageStreamList):
65            _imageStreamList(imageStreamList) {}
66           
67        virtual void apply(osg::Geode& geode)
68        {
69            apply(geode.getStateSet());
70
71            for(unsigned int i=0;i<geode.getNumDrawables();++i)
72            {
73                apply(geode.getDrawable(i)->getStateSet());
74            }
75       
76            traverse(geode);
77        }
78
79        virtual void apply(osg::Node& node)
80        {
81            apply(node.getStateSet());
82            traverse(node);
83        }
84       
85        inline void apply(osg::StateSet* stateset)
86        {
87            if (!stateset) return;
88           
89            osg::StateAttribute* attr = stateset->getTextureAttribute(0,osg::StateAttribute::TEXTURE);
90            if (attr)
91            {
92                osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(attr);
93                if (texture2D) apply(dynamic_cast<osg::ImageStream*>(texture2D->getImage()));
94
95                osg::TextureRectangle* textureRec = dynamic_cast<osg::TextureRectangle*>(attr);
96                if (textureRec) apply(dynamic_cast<osg::ImageStream*>(textureRec->getImage()));
97            }
98        }
99       
100        inline void apply(osg::ImageStream* imagestream)
101        {
102            if (imagestream)
103            {
104                _imageStreamList.push_back(imagestream);
105                s_imageStream = imagestream;
106            }
107        }
108       
109        ImageStreamList& _imageStreamList;
110    };
111
112
113    ImageStreamList _imageStreamList;
114   
115};
116
117
118
119void MovieEventHandler::set(osg::Node* node)
120{
121    _imageStreamList.clear();
122    if (node)
123    {
124        FindImageStreamsVisitor fisv(_imageStreamList);
125        node->accept(fisv);
126    }
127}
128
129
130bool MovieEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv)
131{
132    switch(ea.getEventType())
133    {
134        case(osgGA::GUIEventAdapter::MOVE):
135        case(osgGA::GUIEventAdapter::PUSH):
136        case(osgGA::GUIEventAdapter::RELEASE):
137        {
138            osgProducer::Viewer* viewer = dynamic_cast<osgProducer::Viewer*>(&aa);
139            osgUtil::IntersectVisitor::HitList hlist;
140            if (viewer->computeIntersections(ea.getX(),ea.getY(), nv->getNodePath().back(), hlist))
141            {
142                if (!hlist.empty())
143                {
144                    // use the nearest intersection                 
145                    osgUtil::Hit& hit = hlist.front();
146                    osg::Drawable* drawable = hit.getDrawable();
147                    osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
148                    osg::Vec3Array* vertices = geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
149
150                    if (vertices)
151                    {
152                        // get the vertex indices.
153                        const osgUtil::Hit::VecIndexList& vil = hit.getVecIndexList();
154                       
155                        if (vil.size()==3)
156                        {
157
158                            int i1 = vil[0];
159                            int i2 = vil[1];
160                            int i3 = vil[2];
161                            osg::Vec3 v1 = (*vertices)[i1];
162                            osg::Vec3 v2 = (*vertices)[i2];
163                            osg::Vec3 v3 = (*vertices)[i3];
164                            osg::Vec3 v = hit.getLocalIntersectPoint();
165                            osg::Vec3 p1 = hit.getLocalLineSegment()->start();
166                            osg::Vec3 p2 = hit.getLocalLineSegment()->end();
167                           
168                            osg::Vec3 p12 = p1-p2;
169                            osg::Vec3 v13 = v1-v3;
170                            osg::Vec3 v23 = v2-v3;
171                            osg::Vec3 p1v3 = p1-v3;
172                           
173                            osg::Matrix matrix(p12.x(), v13.x(), v23.x(), 0.0,
174                                               p12.y(), v13.y(), v23.y(), 0.0,
175                                               p12.z(), v13.z(), v23.z(), 0.0,
176                                               0.0,    0.0,    0.0,    1.0);
177                                               
178                            osg::Matrix inverse;
179                            inverse.invert(matrix);
180                           
181                            osg::Vec3 ratio = inverse*p1v3;
182
183                            // extract the baricentric coordinates.                           
184                            float r1 = ratio.y();
185                            float r2 = ratio.z();
186                            float r3 = 1.0f-r1-r2;
187
188                            osg::Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
189                            osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
190                            if (texcoords_Vec2Array)
191                            {
192                                // we have tex coord array so now we can compute the final tex coord at the point of intersection.                               
193                                osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
194                                osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
195                                osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
196                                osg::Vec2 tc = tc1*r1 + tc2*r2 + tc3*r3;
197                               
198                                osg::notify(osg::NOTICE)<<"We hit tex coords "<<tc<<std::endl;
199                               
200                            }
201                           
202                           
203                        }
204                        else
205                        {
206                            osg::notify(osg::NOTICE)<<"Hit but insufficient indices to work with";
207                        }
208   
209                    }
210
211                }
212            }
213            else
214            {
215                osg::notify(osg::NOTICE)<<"No hit"<<std::endl;
216            }
217            break;
218        }
219        case(osgGA::GUIEventAdapter::KEYDOWN):
220        {
221            if (ea.getKey()=='s')
222            {
223                for(ImageStreamList::iterator itr=_imageStreamList.begin();
224                    itr!=_imageStreamList.end();
225                    ++itr)
226                {
227                    std::cout<<"Play"<<std::endl;
228                     (*itr)->play();
229                }
230                return true;
231            }
232            else if (ea.getKey()=='p')
233            {
234                for(ImageStreamList::iterator itr=_imageStreamList.begin();
235                    itr!=_imageStreamList.end();
236                    ++itr)
237                {
238                    std::cout<<"Pause"<<std::endl;
239                    (*itr)->pause();
240                }
241                return true;
242            }
243            else if (ea.getKey()=='r')
244            {
245                for(ImageStreamList::iterator itr=_imageStreamList.begin();
246                    itr!=_imageStreamList.end();
247                    ++itr)
248                {
249                    std::cout<<"Restart"<<std::endl;
250                    (*itr)->rewind();
251                    (*itr)->play();
252                }
253                return true;
254            }
255            else if (ea.getKey()=='l')
256            {
257                for(ImageStreamList::iterator itr=_imageStreamList.begin();
258                    itr!=_imageStreamList.end();
259                    ++itr)
260                {
261                    if ( (*itr)->getLoopingMode() == osg::ImageStream::LOOPING)
262                    {
263                        std::cout<<"Toggle Looping Off"<<std::endl;
264                        (*itr)->setLoopingMode( osg::ImageStream::NO_LOOPING );
265                    }
266                    else
267                    {
268                        std::cout<<"Toggle Looping On"<<std::endl;
269                        (*itr)->setLoopingMode( osg::ImageStream::LOOPING );
270                    }
271                }
272                return true;
273            }
274            return false;
275        }
276
277        default:
278            return false;
279    }
280    return false;
281}
282
283void MovieEventHandler::getUsage(osg::ApplicationUsage& usage) const
284{
285    usage.addKeyboardMouseBinding("p","Pause movie");
286    usage.addKeyboardMouseBinding("s","Play movie");
287    usage.addKeyboardMouseBinding("r","Restart movie");
288    usage.addKeyboardMouseBinding("l","Toggle looping of movie");
289}
290
291
292osg::Geometry* myCreateTexturedQuadGeometry(const osg::Vec3& pos,float width,float height, osg::Image* image, bool useTextureRectangle)
293{
294    if (useTextureRectangle)
295    {
296        osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(pos,
297                                           osg::Vec3(width,0.0f,0.0f),
298                                           osg::Vec3(0.0f,0.0f,height),
299                                           0.0f,image->t(), image->s(),0.0f);
300
301        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
302                    new osg::TextureRectangle(image),
303                    osg::StateAttribute::ON);
304                   
305        return pictureQuad;
306    }
307    else
308    {
309        osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(pos,
310                                           osg::Vec3(width,0.0f,0.0f),
311                                           osg::Vec3(0.0f,0.0f,height),
312                                           0.0f,1.0f, 1.0f,0.0f);
313                                   
314        osg::Texture2D* texture = new osg::Texture2D(image);
315        texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); 
316                                       
317        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
318                    texture,
319                    osg::StateAttribute::ON);
320
321        return pictureQuad;
322    }
323}
324
325int main(int argc, char** argv)
326{
327    // use an ArgumentParser object to manage the program arguments.
328    osg::ArgumentParser arguments(&argc,argv);
329   
330    // set up the usage document, in case we need to print out how to use this program.
331    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
332    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" example demonstrates the use of ImageStream for rendering movies as textures.");
333    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
334    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
335   
336    bool useTextureRectangle = true;
337    bool useShader = false;
338
339    // construct the viewer.
340    osgProducer::Viewer viewer(arguments);
341   
342    while (arguments.read("--texture2D")) useTextureRectangle=false;
343    while (arguments.read("--shader")) useShader=true;
344
345    // set up the value with sensible default event handlers.
346    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
347
348    // get details on keyboard and mouse bindings used by the viewer.
349    viewer.getUsage(*arguments.getApplicationUsage());
350
351    // if user request help write it out to cout.
352    if (arguments.read("-h") || arguments.read("--help"))
353    {
354        arguments.getApplicationUsage()->write(std::cout);
355        return 1;
356    }
357
358    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
359    osg::Vec3 pos(0.0f,0.0f,0.0f);
360       
361    osg::StateSet* stateset = geode->getOrCreateStateSet();
362    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
363
364    if (useShader)
365    {
366        //useTextureRectangle = false;
367       
368        static const char *shaderSourceTextureRec = {
369            "uniform vec4 cutoff_color;\n"
370            "uniform samplerRect movie_texture;\n"
371            "void main(void)\n"
372            "{\n"
373            "    vec4 texture_color = textureRect(movie_texture, gl_TexCoord[0]); \n"
374            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
375            "    gl_FragColor = texture_color;\n"
376            "}\n"
377        };
378
379        static const char *shaderSourceTexture2D = {
380            "uniform vec4 cutoff_color;\n"
381            "uniform sampler2D movie_texture;\n"
382            "void main(void)\n"
383            "{\n"
384            "    vec4 texture_color = texture2D(movie_texture, gl_TexCoord[0]); \n"
385            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
386            "    gl_FragColor = texture_color;\n"
387            "}\n"
388        };
389
390        osg::Program* program = new osg::Program;
391       
392        program->addShader(new osg::Shader(osg::Shader::FRAGMENT,
393                                           useTextureRectangle ? shaderSourceTextureRec : shaderSourceTexture2D));
394
395        stateset->addUniform(new osg::Uniform("cutoff_color",osg::Vec4(0.1f,0.1f,0.1f,1.0f)));
396        stateset->addUniform(new osg::Uniform("movie_texture",0));
397
398        stateset->setAttribute(program);
399
400    }
401
402
403    for(int i=1;i<arguments.argc();++i)
404    {
405        if (arguments.isString(i))
406        {
407            osg::Image* image = osgDB::readImageFile(arguments[i]);
408            osg::ImageStream* imagestream = dynamic_cast<osg::ImageStream*>(image);
409            if (imagestream) imagestream->play();
410           
411            if (image)
412            {
413                geode->addDrawable(myCreateTexturedQuadGeometry(pos,image->s(),image->t(),image, useTextureRectangle));
414               
415                pos.z() += image->t()*1.5f;
416            }
417            else
418            {
419                std::cout<<"Unable to read file "<<arguments[i]<<std::endl;
420            }           
421        }
422    }
423   
424    if (geode->getNumDrawables()==0)
425    {
426        // nothing loaded.
427        return 1;
428    }
429   
430   
431
432    // pass the model to the MovieEventHandler so it can pick out ImageStream's to manipulate.
433    MovieEventHandler* meh = new MovieEventHandler();
434    geode->setEventCallback(meh);
435    meh->set(geode.get());
436
437    // report any errors if they have occured when parsing the program aguments.
438    if (arguments.errors())
439    {
440        arguments.writeErrorMessages(std::cout);
441        return 1;
442    }
443   
444    if (arguments.argc()<=1)
445    {
446        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
447        return 1;
448    }
449
450
451    // any option left unread are converted into errors to write out later.
452    arguments.reportRemainingOptionsAsUnrecognized();
453
454    // report any errors if they have occured when parsing the program aguments.
455    if (arguments.errors())
456    {
457        arguments.writeErrorMessages(std::cout);
458    }
459/*
460    // set up a post swap callback to flush deleted GL objects and compile new GL objects           
461    for(unsigned int cameraNum=0;cameraNum<viewer.getNumberOfCameras();++cameraNum)
462    {
463        Producer::Camera* camera=viewer.getCamera(cameraNum);
464        camera->addPostSwapCallback(new PostSwapFinishCallback());
465    }
466*/
467    // set the scene to render
468    viewer.setSceneData(geode.get());
469
470    // create the windows and run the threads.
471    viewer.realize();
472
473    while( !viewer.done() )
474    {
475        // wait for all cull and draw threads to complete.
476        viewer.sync();
477       
478        // update the scene by traversing it with the the update visitor which will
479        // call all node update callbacks and animations.
480        viewer.update();
481         
482        // fire off the cull and draw traversals of the scene.
483        viewer.frame();
484       
485    }
486   
487    // wait for all cull and draw threads to complete before exit.
488    viewer.sync();
489
490    return 0;
491
492
493}
Note: See TracBrowser for help on using the browser.