diff --git a/examples/osgmovie/GNUmakefile b/examples/osgmovie/GNUmakefile new file mode 100644 index 000000000..4881e5453 --- /dev/null +++ b/examples/osgmovie/GNUmakefile @@ -0,0 +1,19 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgmovie.cpp MpegImageStream.cpp\ + +OTHER_LIBS += -lmpeg3 -lpthread +LIBS += -losgProducer -lProducer -losgText -losgGA -losgDB -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +INSTFILES = \ + $(CXXFILES)\ + GNUmakefile.inst=GNUmakefile + +EXEC = osgmovie + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules + diff --git a/examples/osgmovie/GNUmakefile.inst b/examples/osgmovie/GNUmakefile.inst new file mode 100644 index 000000000..e2202afc8 --- /dev/null +++ b/examples/osgmovie/GNUmakefile.inst @@ -0,0 +1,14 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgmovie.cpp MpegImageStream.cpp\ + +OTHER_LIBS += -lmpeg3 -lpthread +LIBS += -losgProducer -lProducer -losgDB -losgText -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +EXEC = osgmovie + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules diff --git a/examples/osgmovie/ImageStream.h b/examples/osgmovie/ImageStream.h new file mode 100644 index 000000000..9e56ca3e1 --- /dev/null +++ b/examples/osgmovie/ImageStream.h @@ -0,0 +1,72 @@ +// -*-c++-*- + +/* + * Copyright (C) 2001 Ulrich Hertlein + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IMAGESTREAM_H_ +#define _IMAGESTREAM_H_ + +#include +#include + +namespace osg { + + /** + * Image Stream class. + */ + class SG_EXPORT ImageStream : public Image + { + public: + ImageStream() { + _texMat = new TexMat; + _texMat->ref(); + } + + virtual Object* clone() const { return new ImageStream; } + virtual bool isSameKindAs(const Object* obj) const { + return dynamic_cast(obj) != NULL; + } + virtual const char* className() const { return "ImageStream"; } + + /// Return suitable texture matrix. + inline const TexMat* getTexMat() const { return _texMat; } + + /// Start or continue MPEG stream. + virtual inline void start() {} + + /// Stop MPEG stream. + virtual inline void stop() {} + + /// Rewind MPEG stream. + virtual inline void rewind() {} + + protected: + virtual ~ImageStream() { + _texMat->unref(); + } + + TexMat* _texMat; + }; + +} // namespace + +#endif diff --git a/examples/osgmovie/MpegImageStream.cpp b/examples/osgmovie/MpegImageStream.cpp new file mode 100644 index 000000000..960c236c9 --- /dev/null +++ b/examples/osgmovie/MpegImageStream.cpp @@ -0,0 +1,257 @@ +// -*-c++-*- + +/* + * Copyright (C) 2001 Ulrich Hertlein + * + * Uses libmpeg3 by Adam Williams + * See http://www.heroinewarrior.com + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "MpegImageStream.h" +#include + +#include +#include +#include + +#include "libmpeg3/libmpeg3.h" + +using namespace osg; + +#define IDLE_TIMEOUT 150000L + + +// Constructor: setup and start thread +MpegImageStream::MpegImageStream(const char* fileName) : ImageStream() +{ + _useMMX = true; + _fps = 0.0f; + _frames = 0; + _len = 0; + + for (int i = 0; i < NUM_CMD_INDEX; i++) + _cmd[i] = THREAD_IDLE; + _wrIndex = _rdIndex = 0; + + ::pthread_mutex_init(&_mutex, NULL); + ::pthread_create(&_id, NULL, MpegImageStream::s_decode, this); + + if (fileName) + setFileName(fileName); +} + + +// Deconstructor: stop and terminate thread +MpegImageStream::~MpegImageStream() +{ + stop(); + setCmd(THREAD_QUIT); + ::pthread_join(_id, NULL); + ::pthread_mutex_destroy(&_mutex); +} + + +// Set command +void MpegImageStream::setCmd(ThreadCommand cmd) +{ + lock(); + _cmd[_wrIndex] = cmd; + _wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX; + unlock(); +} + + +// Get command +MpegImageStream::ThreadCommand MpegImageStream::getCmd() +{ + ThreadCommand cmd = THREAD_IDLE; + lock(); + if (_rdIndex != _wrIndex) { + cmd = _cmd[_rdIndex]; + _rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX; + } + unlock(); + return cmd; +} + + +/* + * Decoder thread + */ +void* MpegImageStream::s_decode(void* vp) +{ + return ((MpegImageStream*) vp)->decode(vp); +} + +void* MpegImageStream::decode(void*) +{ + bool playing = false; + mpeg3_t* mpg = NULL; + int str = 0; + float t0 = 0.0f; + unsigned long delay = 0; + unsigned char** rows = NULL; + + bool done = false; + while (!done) { + + // Handle commands + ThreadCommand cmd = getCmd(); + if (cmd != THREAD_IDLE) { + switch (cmd) { + case THREAD_START: // Start or continue stream + playing = false; + if (!mpg) { + mpg = mpeg3_open((char*) _fileName.c_str()); + if (!mpg) { + osg::notify(WARN) << "Unable to open " << _fileName << std::endl; + continue; + } + + if (!mpeg3_has_video(mpg)) { + osg::notify(WARN) << "No video streams in" << _fileName << std::endl; + continue; + } + if (mpeg3_has_audio(mpg)) { + std::cerr << "Stream has audio" << std::endl; + } + + mpeg3_set_cpus(mpg, 1); + mpeg3_set_mmx(mpg, _useMMX); + + str = 0; //mpeg3_total_vstreams(mpg) - 1; + + _fps = mpeg3_frame_rate(mpg, str); + _frames = mpeg3_video_frames(mpg, str); + _len = (float) _frames / _fps; + + t0 = 0.0f; + delay = (unsigned long) ((1.0f / _fps) * 1000L); + + int s = mpeg3_video_width(mpg, str); + int t = mpeg3_video_height(mpg, str); + + // Calculate texture size + // these are also calculated and stored within osg::Texture but + // too late (on the first apply) to be of any use... + int texWidth = 1; + for (; texWidth < s; texWidth <<= 1) + ; + int texHeight = 1; + for (; texHeight < t; texHeight <<= 1) + ; + + + // Allocate image data + // maybe use BGR888 and save some conversion somewhere? + unsigned char* data = (unsigned char*) ::malloc(s * t * 3); + + + setImage(s, t, 0, + GL_RGB, + GL_RGB, GL_UNSIGNED_BYTE, data, + osg::Image::USE_MALLOC_FREE); + + // Allocate decoder rows + // documentation says we need add'l bytes at the end of each + // row for MMX but this is more efficient and works so far. + rows = (unsigned char**) ::malloc(t * sizeof(unsigned char*)); + unsigned char* dp = data; + for (int i = 0; i < t; i++) { + rows[i] = dp; + dp += (s * 3); + } + + +#if 0 + // Setup texture matrix + Matrix mat; + mat.makeScale((float) s / (float) texWidth, + ((float) t / (float) texHeight) * -1.0f, 1.0f); + mat = mat * Matrix::translate(0.0f, (float) t / (float) texHeight, 0.0f); + _texMat->setMatrix(mat); +#else + _texMat->setMatrix(osg::Matrix::scale(s,-t,1.0f)*osg::Matrix::translate(0.0f,t,0.0f)); +#endif + // XXX + std::cerr << _frames << " @ " << _fps << " " << _len << "s" << std::endl; + std::cerr << "img " << s << "x" << t << std::endl; + std::cerr << "tex " << texWidth << "x" << texHeight << std::endl; + std::cerr << "delay " << delay << "ms" << std::endl; + } + playing = true; + break; + case THREAD_STOP: // XXX + std::cerr << "stop at " << t0 << std::endl; + playing = false; + break; + case THREAD_REWIND: // XXX + t0 = 0.0; + mpeg3_seek_percentage(mpg, 0.0); + break; + case THREAD_CLOSE: // Stop and close + playing = false; + if (mpg) { + mpeg3_close(mpg); + mpg = NULL; + } + break; + case THREAD_QUIT: // XXX + std::cerr << "quit" << std::endl; + done = true; + break; + default: + osg::notify(osg::WARN) << "Unknown command " << cmd << std::endl; + break; + } + } + + if (playing) { + // XXX needs more work to be real-time + mpeg3_read_frame(mpg, rows, + 0, 0, _s, _t, + _s, _t, + MPEG3_RGB888, str); + dirty(); //Image(); + + if (mpeg3_get_time(mpg) > _len) { + rewind(); //stop(); + } + else + ::usleep(delay); + } + else { + ::usleep(IDLE_TIMEOUT); + } + } + + // Cleanup decoder + if (mpg) { + mpeg3_close(mpg); + mpg = NULL; + } + if (rows) { + ::free(rows); + rows = NULL; + } + + return NULL; +} diff --git a/examples/osgmovie/MpegImageStream.h b/examples/osgmovie/MpegImageStream.h new file mode 100644 index 000000000..50fd6436c --- /dev/null +++ b/examples/osgmovie/MpegImageStream.h @@ -0,0 +1,121 @@ +// -*-c++-*- + +/* + * Copyright (C) 2001 Ulrich Hertlein + * + * Uses libmpeg3 by Adam Williams + * See http://www.heroinewarrior.com + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MPEGIMAGESTREAM_H_ +#define _MPEGIMAGESTREAM_H_ + +#include "ImageStream.h" + +#include + +#define NUM_CMD_INDEX 4 + +namespace osg { + + /** + * MPEG1/2 Image Stream class. + */ + class SG_EXPORT MpegImageStream : public ImageStream + { + public: + MpegImageStream(const char* fileName = NULL); + + virtual Object* clone() const { return new MpegImageStream; } + virtual bool isSameKindAs(const Object* obj) const { + return dynamic_cast(obj) != NULL; + } + virtual const char* className() const { return "MpegImageStream"; } + + /// Start or continue stream. + virtual inline void start() { setCmd(THREAD_START); } + + /// Stop stream at current position. + virtual inline void stop() { setCmd(THREAD_STOP); } + + /// Rewind stream to beginning. + virtual inline void rewind() { setCmd(THREAD_REWIND); } + + /// Enable/disable MMX. + inline void enableMMX(bool b) { _useMMX = b; } + + /** + * Set frame rate in fps. + * This is overwritten by the actual frame rate of the stream when + * it is opened. + */ + inline void setFrameRate(float fps) { _fps = (fps < 0.0f ? 0.0f : fps); } + + /// Get frame rate in fps. + inline float getFrameRate() const { return _fps; } + + /// Get number of frames. + inline long getNumFrames() const { return _frames; } + + /// Get total length in seconds. + inline float getLength() const { return _len; } + + protected: + virtual ~MpegImageStream(); + + private: + bool _useMMX; + float _fps; + long _frames; + float _len; + + enum ThreadCommand { + THREAD_IDLE = 0, + THREAD_START, + THREAD_STOP, + THREAD_REWIND, + THREAD_CLOSE, + THREAD_QUIT + }; + ThreadCommand _cmd[NUM_CMD_INDEX]; + int _wrIndex, _rdIndex; + + pthread_mutex_t _mutex; + pthread_t _id; + + // Lock/unlock object. + inline void lock() { ::pthread_mutex_lock(&_mutex); } + inline void unlock() { ::pthread_mutex_unlock(&_mutex); } + + /// Set command. + void setCmd(ThreadCommand cmd); + + /// Get command. + ThreadCommand getCmd(); + + /// Decoder hook. + static void* s_decode(void*); + void* decode(void*); + }; + +} // namespace + +#endif diff --git a/examples/osgmovie/osgmovie.cpp b/examples/osgmovie/osgmovie.cpp new file mode 100644 index 000000000..675e24b9e --- /dev/null +++ b/examples/osgmovie/osgmovie.cpp @@ -0,0 +1,208 @@ +// -*-c++-*- + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "MpegImageStream.h" + + +/* + * Create morphed textured geometry + */ +osg::Geode* morphGeom(osg::Vec3Array* coords, + osg::Vec3Array* normals, + osg::Vec2Array* texCoords, + osg::Image* image, + osg::TexMat* texMat) +{ + /* + * GeoSet + */ + osg::Geometry* gset = new osg::Geometry(); + + gset->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,0,4)); + + gset->setVertexArray(coords); + + gset->setNormalArray(normals); + gset->setNormalBinding(osg::Geometry::BIND_OVERALL); + + gset->setTexCoordArray(0,texCoords); + + /* + * StateSet + */ + osg::StateSet* state = new osg::StateSet; + + osg::Material* mtl = new osg::Material(); + osg::Vec4 white( 1.0f, 1.0f, 1.0f, 1.0f ); + mtl->setEmission( osg::Material::FRONT_AND_BACK, white ); + mtl->setAmbient( osg::Material::FRONT_AND_BACK, white ); + mtl->setDiffuse( osg::Material::FRONT_AND_BACK, white ); + mtl->setSpecular( osg::Material::FRONT_AND_BACK, white ); + state->setAttribute(mtl); + + //osg::Texture2D* tex = new osg::Texture2D; + osg::TextureRectangle* tex = new osg::TextureRectangle; + if (!image) { + image = osgDB::readImageFile("lz.rgb"); + } + tex->setImage(image); + tex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); + state->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); + + if (texMat) + state->setTextureAttributeAndModes(0, texMat, osg::StateAttribute::ON); + + // don't cull faces + osg::CullFace* cull = new osg::CullFace; + state->setAttributeAndModes(cull, osg::StateAttribute::OFF); + + /* + * Geode + */ + osg::Geode* geode = new osg::Geode; + geode->setStateSet( state ); + geode->addDrawable( gset ); + + return geode; +} + + +/* + * Main + */ +int main(int argc, char** argv) +{ + // coordinates + osg::Vec3Array* coords = new osg::Vec3Array(4); + (*coords)[0].set( -1.0f, 0.0f, -1.0f ); + (*coords)[1].set( 1.0f, 0.0f, -1.0f ); + (*coords)[2].set( 1.0f, 0.0f, 1.0f ); + (*coords)[3].set( -1.0f, 0.0f, 1.0f ); + + + // normals + osg::Vec3Array* normals = new osg::Vec3Array(1); + (*normals)[0].set( 0.0f, 1.0f, 0.0f ); + + // texture coordinates + osg::Vec2Array* texCoords = new osg::Vec2Array(4); + (*texCoords)[0].set(0.0f, 0.0f); + (*texCoords)[1].set(1.0f, 0.0f); + (*texCoords)[2].set(1.0f, 1.0f); + (*texCoords)[3].set(0.0f, 1.0f); + + + // open MpegImageStream + osg::MpegImageStream* mpeg = NULL; + osg::TexMat* texMat = NULL; + if (argc > 1) { + mpeg = new osg::MpegImageStream(argv[1]); + mpeg->start(); + texMat = (osg::TexMat*) mpeg->getTexMat(); + } + + // Create morphed geometry + osg::Geode* geode = morphGeom(coords, + normals, texCoords, mpeg, texMat); + //coordMorph.addGeode(geode); + + + + + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); + + // set up the usage document, in case we need to print out how to use this program. + arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models."); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); + arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); + + + // construct the viewer. + osgProducer::Viewer viewer(arguments); + + // set up the value with sensible default event handlers. + viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS); + + // get details on keyboard and mouse bindings used by the viewer. + viewer.getUsage(*arguments.getApplicationUsage()); + + // if user request help write it out to cout. + if (arguments.read("-h") || arguments.read("--help")) + { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + if (arguments.argc()<=1) + { + arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); + return 1; + } + + + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + } + + // set the scene to render + viewer.setSceneData(geode); + + // create the windows and run the threads. + viewer.realize(); + + while( !viewer.done() ) + { + // wait for all cull and draw threads to complete. + viewer.sync(); + + // update the geoemtry + //coordMorph.update(viewer.getFrameStamp()->getReferenceTime()); + + // update the scene by traversing it with the the update visitor which will + // call all node update callbacks and animations. + viewer.update(); + + // fire off the cull and draw traversals of the scene. + viewer.frame(); + + } + + // wait for all cull and draw threads to complete before exit. + viewer.sync(); + + return 0; + + +}