// // Vrml 97 library // Copyright (C) 1998 Chris Morley // // %W% %G% // VrmlNodeMovieTexture.cpp #include "VrmlNodeMovieTexture.h" #include "VRMLImage.h" #include "MathUtils.h" #include "System.h" #include "VrmlNodeType.h" #include "VrmlScene.h" #include "Viewer.h" #include "Doc.h" #if defined AW_NEW_IOSTREAMS # include #else # include #endif static VrmlNode *creator( VrmlScene *s ) { return new VrmlNodeMovieTexture(s); } // Define the built in VrmlNodeType:: "MovieTexture" fields VrmlNodeType *VrmlNodeMovieTexture::defineType(VrmlNodeType *t) { static VrmlNodeType *st = 0; if (! t) { if (st) return st; t = st = new VrmlNodeType("MovieTexture", creator); } VrmlNodeTexture::defineType(t); // Parent class t->addExposedField("loop", VrmlField::SFBOOL); t->addExposedField("speed", VrmlField::SFFLOAT); t->addExposedField("startTime", VrmlField::SFTIME); t->addExposedField("stopTime", VrmlField::SFTIME); t->addExposedField("url", VrmlField::MFSTRING); t->addField("repeatS", VrmlField::SFBOOL); t->addField("repeatT", VrmlField::SFBOOL); t->addEventOut("duration_changed", VrmlField::SFTIME); t->addEventOut("isActive", VrmlField::SFBOOL); return t; } VrmlNodeType *VrmlNodeMovieTexture::nodeType() const { return defineType(0); } VrmlNodeMovieTexture::VrmlNodeMovieTexture(VrmlScene *scene) : VrmlNodeTexture(scene), d_loop(false), d_speed(1.0), d_repeatS(true), d_repeatT(true), d_image(0), d_frame(0), d_lastFrame(-1), d_lastFrameTime(-1.0), d_texObject(0) { if (d_scene) d_scene->addMovie( this ); } VrmlNodeMovieTexture::~VrmlNodeMovieTexture() { //if (d_texObject) d_viewer->removeTextureObject(d_texObject); if (d_scene) d_scene->removeMovie( this ); delete d_image; } VrmlNode *VrmlNodeMovieTexture::cloneMe() const { return new VrmlNodeMovieTexture(*this); } VrmlNodeMovieTexture* VrmlNodeMovieTexture::toMovieTexture() const { return (VrmlNodeMovieTexture*) this; } void VrmlNodeMovieTexture::addToScene(VrmlScene *s, const char *rel) { if (d_scene != s && (d_scene = s) != 0) d_scene->addMovie(this); VrmlNodeTexture::addToScene(s, rel); } ostream& VrmlNodeMovieTexture::printFields(ostream& os, int indent) { if (d_loop.get()) PRINT_FIELD(loop); if (!FPEQUAL(d_speed.get(), 1.0)) PRINT_FIELD(speed); if (!FPZERO(d_startTime.get())) PRINT_FIELD(startTime); if (!FPZERO(d_stopTime.get())) PRINT_FIELD(stopTime); if (d_url.get()) PRINT_FIELD(url); if (! d_repeatS.get()) PRINT_FIELD(repeatS); if (! d_repeatT.get()) PRINT_FIELD(repeatT); return os; } void VrmlNodeMovieTexture::update( VrmlSFTime &timeNow ) { if ( isModified() ) { if (d_image) { const char *imageUrl = d_image->url(); int imageLen = (int)strlen(imageUrl); int i, nUrls = d_url.size(); for (i=0; i len && strcmp(imageUrl+imageLen-len, d_url[i]) == 0)) break; } // if (d_image->url() not in d_url list) ... if (i == nUrls) { delete d_image; d_image = 0; } } } // Load the movie if needed (should check startTime...) if (! d_image && d_url.size() > 0) { Doc relDoc( d_relativeUrl.get() ); Doc *rel = d_relativeUrl.get() ? &relDoc : d_scene->urlDoc(); d_image = new Image; if ( ! d_image->tryURLs( d_url.size(), d_url.get(), rel ) ) cerr << "Error: couldn't read MovieTexture from URL " << d_url << endl; int nFrames = d_image->nFrames(); d_duration = (nFrames >= 0) ? nFrames : -1; eventOut( timeNow.get(), "duration_changed", d_duration ); d_frame = (d_speed.get() >= 0) ? 0 : nFrames-1; //theSystem->debug("MovieTexture.%s loaded %d frames\n", name(), nFrames); } // No pictures to show if (! d_image || d_image->nFrames() == 0) return; // Become active at the first tick at or after startTime if either // the valid stopTime hasn't passed or we are looping. if (! d_isActive.get() && d_startTime.get() <= timeNow.get() && d_startTime.get() >= d_lastFrameTime && ( (d_stopTime.get() < d_startTime.get() || // valid stopTime d_stopTime.get() > timeNow.get()) || // hasn't passed d_loop.get() )) { //theSystem->debug("MovieTexture.%s::isActive TRUE\n", name()); d_isActive.set(true); eventOut( timeNow.get(), "isActive", d_isActive ); d_lastFrameTime = timeNow.get(); d_frame = (d_speed.get() >= 0) ? 0 : d_image->nFrames() - 1; setModified(); } // Check whether stopTime has passed else if ( d_isActive.get() && (( d_stopTime.get() > d_startTime.get() && d_stopTime.get() <= timeNow.get() ) || d_frame < 0)) { //theSystem->debug("MovieTexture.%s::isActive FALSE\n", name()); d_isActive.set(false); eventOut( timeNow.get(), "isActive", d_isActive ); setModified(); } // Check whether the frame should be advanced else if ( d_isActive.get() && d_lastFrameTime + fabs(double(d_speed.get())) <= timeNow.get() ) { if (d_speed.get() < 0.0) --d_frame; else ++d_frame; //theSystem->debug("MovieTexture.%s::frame %d\n", name(), d_frame); d_lastFrameTime = timeNow.get(); setModified(); } // Tell the scene when the next update is needed. if (d_isActive.get()) { double d = d_lastFrameTime + fabs(double(d_speed.get())) - timeNow.get(); d_scene->setDelta( 0.9 * d ); } } // Ignore set_speed when active. void VrmlNodeMovieTexture::eventIn(double timeStamp, const char *eventName, const VrmlField *fieldValue) { const char *origEventName = eventName; if ( strncmp(eventName, "set_", 4) == 0 ) eventName += 4; // Ignore set_speed when active if ( strcmp(eventName,"speed") == 0 ) { if (! d_isActive.get()) { setField(eventName, *fieldValue); eventOut(timeStamp, "speed_changed", *fieldValue); setModified(); } } // Let the generic code handle the rest. else VrmlNode::eventIn( timeStamp, origEventName, fieldValue ); } // Render a frame if there is one available. void VrmlNodeMovieTexture::render(Viewer *viewer) { //theSystem->debug("MovieTexture.%s::render frame %d\n", name(), d_frame); if ( ! d_image || d_frame < 0 ) return; unsigned char *pix = d_image->pixels( d_frame ); if ( d_frame != d_lastFrame && d_texObject ) { viewer->removeTextureObject( d_texObject ); d_texObject = 0; } if ( ! pix ) { d_frame = -1; } else if ( d_texObject ) { viewer->insertTextureReference( d_texObject, d_image->nc() ); } else { // Ensure image dimensions are powers of 2 (move to VrmlNodeTexture...) int sizes[] = { 2, 4, 8, 16, 32, 64, 128, 256 }; int nSizes = sizeof(sizes) / sizeof(int); int w = d_image->w(); int h = d_image->h(); int i, j; for (i=0; i 0 && j > 0) { // Always scale images down in size and reuse the same pixel memory. if (w != sizes[i-1] || h != sizes[j-1]) { viewer->scaleTexture( w, h, sizes[i-1], sizes[j-1], d_image->nc(), pix ); d_image->setSize( sizes[i-1], sizes[j-1] ); } d_texObject = viewer->insertTexture(d_image->w(), d_image->h(), d_image->nc(), d_repeatS.get(), d_repeatT.get(), pix, ! d_isActive.get() ); } } d_lastFrame = d_frame; clearModified(); } int VrmlNodeMovieTexture::nComponents() { return d_image ? d_image->nc() : 0; } int VrmlNodeMovieTexture::width() { return d_image ? d_image->w() : 0; } int VrmlNodeMovieTexture::height() { return d_image ? d_image->h() : 0; } int VrmlNodeMovieTexture::nFrames() { return d_image ? d_image->nFrames() : 0; } unsigned char* VrmlNodeMovieTexture::pixels() { return d_image ? d_image->pixels() : 0; } // Set the value of one of the node fields. void VrmlNodeMovieTexture::setField(const char *fieldName, const VrmlField &fieldValue) { if TRY_FIELD(loop, SFBool) else if TRY_FIELD(speed, SFFloat) else if TRY_FIELD(startTime, SFTime) else if TRY_FIELD(stopTime, SFTime) else if TRY_FIELD(url, MFString) else if TRY_FIELD(repeatS, SFBool) else if TRY_FIELD(repeatT, SFBool) else VrmlNode::setField(fieldName, fieldValue); }