Added osgmovie example, derived from Ulrich Hertlien's original videotex

example, and brought up to date.
This commit is contained in:
Robert Osfield 2004-01-30 14:06:29 +00:00
parent a8b772c09a
commit 9772b21e35
6 changed files with 691 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,72 @@
// -*-c++-*-
/*
* Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
*
* 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 <osg/Image>
#include <osg/TexMat>
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<const ImageStream*>(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

View File

@ -0,0 +1,257 @@
// -*-c++-*-
/*
* Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
*
* 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 <osg/Notify>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#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;
}

View File

@ -0,0 +1,121 @@
// -*-c++-*-
/*
* Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
*
* 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 <pthread.h>
#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<const MpegImageStream*>(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

View File

@ -0,0 +1,208 @@
// -*-c++-*-
#include <osgProducer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/CullFace>
#include <osgGA/TrackballManipulator>
#include <math.h>
#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;
}