From c7bf98cca9a7c3bb0c52caa91a3d05e25fc6b648 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 14 Feb 2011 15:04:40 +0000 Subject: [PATCH] From Jan Peciva, "Improvements: - Use OSG routines to load images instead of simage library (removes dependency on 3rdParty simage library). Applies to Coin Inventor clone only. " --- .../Inventor/ConvertFromInventor.cpp | 327 +++++++++++++++++- src/osgPlugins/Inventor/ConvertFromInventor.h | 4 + src/osgPlugins/Inventor/ReaderWriterIV.cpp | 3 + 3 files changed, 332 insertions(+), 2 deletions(-) diff --git a/src/osgPlugins/Inventor/ConvertFromInventor.cpp b/src/osgPlugins/Inventor/ConvertFromInventor.cpp index a2145ede7..765540197 100644 --- a/src/osgPlugins/Inventor/ConvertFromInventor.cpp +++ b/src/osgPlugins/Inventor/ConvertFromInventor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include // Inventor headers @@ -52,6 +53,7 @@ #include #include #include +#include #endif #if defined(__COIN__) && (COIN_MAJOR_VERSION >= 3 || \ @@ -983,6 +985,318 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, return SoCallbackAction::CONTINUE; } +/////////////////////////////////////////////////////////////// + +#ifdef __COIN__ + +// +// Following classes can be used for redirecting texture loading from Coin routines +// that use simage library to native OSG routines. This removes the dependency +// on simage and using the same routines by OSG and Coin may provide some +// advantages to programmer as well. +// +// Classes that are loading textures: SoTexture2, SoTexture3 (since Coin 2.0), +// SoVRMLImageTexture (since Coin 2.0), SoVRMLMovieTexture (not yet implemented in Coin) +// +// Principle: SoType::overrideType() can be used for changing +// of the class instantiation method. So let's change it and create our own +// API compatible class that will just not load texture from file. +// + +#include +#include +#include + + +// This macro gives the common API for all overriding classes. +#define OVERRIDE_HEADER(_class_,_parent_) \ +public: \ +\ + static void overrideClass() \ + { \ + if (overrideCounter == 0) { \ + SoType t = _parent_::getClassTypeId(); \ + oldMethod = t.getInstantiationMethod(); \ + SoType::overrideType(t, _class_::createInstance); \ + } \ + overrideCounter++; \ + } \ +\ + static SbBool cancelOverrideClass() \ + { \ + assert(overrideCounter > 0 && #_class_ "::cancelOverride called more times\n" \ + "than " #_class_ "::override"); \ + assert(_parent_::getClassTypeId().getInstantiationMethod() == _class_::createInstance && \ + "Somebody changed " #_parent_ " instantiation method\n" \ + "(probably through SoType::overrideType) and did not restored it."); \ +\ + overrideCounter--; \ + if (overrideCounter == 0) \ + SoType::overrideType(_parent_::getClassTypeId(), oldMethod); \ +\ + return overrideCounter==0; \ + } \ +\ +private: \ +\ + static int overrideCounter; \ +\ + static SoType::instantiationMethod oldMethod; \ +\ + static void* createInstance() \ + { \ + return new _class_; \ + } + +#define OVERRIDE_SRC(_class_) \ +\ +int _class_::overrideCounter = 0; \ +SoType::instantiationMethod _class_::oldMethod + + +class SoTexture2Osg : public SoTexture2 { + OVERRIDE_HEADER(SoTexture2Osg, SoTexture2); +protected: + virtual SbBool readInstance(SoInput *in, unsigned short flags); +}; + + +class SoTexture3Osg : public SoTexture3 { + OVERRIDE_HEADER(SoTexture3Osg, SoTexture3); +protected: + virtual SbBool readInstance(SoInput *in, unsigned short flags); +}; + + +class SoVRMLImageTextureOsg : public SoVRMLImageTexture { + OVERRIDE_HEADER(SoVRMLImageTextureOsg, SoVRMLImageTexture); +protected: + virtual SbBool readInstance(SoInput *in, unsigned short flags); +}; + +OVERRIDE_SRC(SoTexture2Osg); +OVERRIDE_SRC(SoTexture3Osg); +OVERRIDE_SRC(SoVRMLImageTextureOsg); + +static osgDB::ReaderWriter::Options* createOptions() +{ + const SbStringList &soInputDirectories = SoInput::getDirectories(); + osgDB::ReaderWriter::Options *options = new osgDB::ReaderWriter::Options; + osgDB::FilePathList &pathList = options->getDatabasePathList(); + int c = soInputDirectories.getLength(); + for (int i=0; igetString()); + + return options; +} + +static osg::Image* loadImage(const char *fileName, osgDB::ReaderWriter::Options *options) +{ + osg::Image *osgImage = osgDB::readImageFile(fileName, options); + + if (!osgImage) + OSG_WARN << NOTIFY_HEADER << "Could not read texture file '" << fileName << "'."; + + return osgImage; +} + +SbBool SoTexture2Osg::readInstance(SoInput *in, unsigned short flags) +{ + // disable notification and read inherited fields + SbBool oldNotify = filename.enableNotify(FALSE); + SbBool readOK = SoNode::readInstance(in, flags); + this->setReadStatus((int) readOK); + + // if file name given + if (readOK && !filename.isDefault() && filename.getValue() != "") + { + // create options and read the file + osgDB::ReaderWriter::Options *options = createOptions(); + osg::ref_ptr image = loadImage(filename.getValue().getString(), options); + + if (image.valid()) + { + // get image dimensions and data + int nc = osg::Image::computeNumComponents(image->getPixelFormat()); + SbVec2s size(image->s(), image->t()); + unsigned char *bytes = image->data(); + + // disable notification on image while setting data from filename + // as a notify will cause a filename.setDefault(TRUE) + SbBool oldnotify = this->image.enableNotify(FALSE); + this->image.setValue(size, nc, bytes); + this->image.enableNotify(oldnotify); + // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender() + // We can safely ignore this as we are not going to render the scene graph. + } + else + { + // image loading failed -> set readOK + readOK = FALSE; + this->setReadStatus(FALSE); + } + + // write filename, not image + this->image.setDefault(TRUE); + } + + filename.enableNotify(oldNotify); + return readOK; +} + +SbBool SoTexture3Osg::readInstance(SoInput *in, unsigned short flags) +{ + // disable notification and read inherited fields + SbBool oldNotify = filenames.enableNotify(FALSE); + SbBool readOK = SoNode::readInstance(in, flags); + this->setReadStatus((int) readOK); + + // if file name given + int numImages = filenames.getNum(); + if (readOK && !filenames.isDefault() && numImages > 0) + { + // Fail on empty filenames + SbBool sizeError = FALSE; + SbBool retval = FALSE; + SbVec3s volumeSize(0,0,0); + int volumenc = -1; + int i; + for (i=0; ifilenames[i].getLength()==0) break; + + if (i==numImages) + { + // create options + osgDB::ReaderWriter::Options *options = createOptions(); + + for (int n=0; n image = loadImage(filenames[n].getString(), options); + + if (!image.valid()) + { + OSG_WARN << NOTIFY_HEADER << "Could not read texture file #" << n << ": " + << filenames[n].getString() << "\n"; + retval = FALSE; + } + else + { + // get image dimensions and data + int nc = osg::Image::computeNumComponents(image->getPixelFormat()); + SbVec3s size(image->s(), image->t(), image->r()); + if (size[2]==0) + size[2]=1; + unsigned char *imgbytes = image->data(); + + if (this->images.isDefault()) { // First time => allocate memory + volumeSize.setValue(size[0], + size[1], + size[2]*numImages); + volumenc = nc; + this->images.setValue(volumeSize, nc, NULL); + } + else { // Verify size & components + if (size[0] != volumeSize[0] || + size[1] != volumeSize[1] || + //FIXME: always 1 or what? (kintel 20020110) + size[2] != (volumeSize[2]/numImages) || + nc != volumenc) + { + sizeError = TRUE; + retval = FALSE; + + OSG_WARN << NOTIFY_HEADER << "Texture file #" << n << " (" + << filenames[n].getString() << ") has wrong size: " + << "Expected (" << volumeSize[0] << "," << volumeSize[1] << "," + << volumeSize[2] << "," << volumenc << ") got (" + << size[0] << "," << size[1] << "," << size[2] << "," << nc << ")\n"; + } + } + + if (!sizeError) + { + // disable notification on images while setting data from the + // filenames as a notify will cause a filenames.setDefault(TRUE). + SbBool oldnotify = this->images.enableNotify(FALSE); + unsigned char *volbytes = this->images.startEditing(volumeSize, + volumenc); + memcpy(volbytes+int(size[0])*int(size[1])*int(size[2])*nc*n, + imgbytes, int(size[0])*int(size[1])*int(size[2])*nc); + this->images.finishEditing(); + this->images.enableNotify(oldnotify); + // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender() + // We can safely ignore this as we are not going to render the scene graph. + retval = TRUE; + } + } + } + } + + if (!retval) + { + // if image loading failed, set read status, + // but not set readOK to false (according to Coin source code) + this->setReadStatus(FALSE); + } + + // write filename, not image + this->images.setDefault(TRUE); + } + + filenames.enableNotify(oldNotify); + return readOK; +} + +SbBool SoVRMLImageTextureOsg::readInstance(SoInput *in, unsigned short flags) +{ + // disable notification and read inherited fields + SbBool oldNotify = url.enableNotify(FALSE); + SbBool readOK = SoNode::readInstance(in, flags); + this->setReadStatus((int) readOK); + + if (readOK) { + + // create options and read the file + osgDB::ReaderWriter::Options *options = createOptions(); + + SbBool retval = TRUE; + if (url.getNum() && url[0].getLength()) { + osg::ref_ptr image = loadImage(url[0].getString(), options); + retval = image->valid(); + if (!image->valid()) + { + OSG_WARN << "Could not read texture file: " << url[0].getString() << std::endl; + this->setReadStatus(FALSE); + } + else + { + // get image dimensions and data + int nc = osg::Image::computeNumComponents(image->getPixelFormat()); + SbVec2s size(image->s(), image->t()); + unsigned char *bytes = image->data(); + + SbImage ivImage(bytes, size, nc); + + // disable notification on image while setting data from filename + // as a notify will cause a filename.setDefault(TRUE) + //SbBool oldnotify = this->image.enableNotify(FALSE); <- difficult to implement for SoVRMLImageTexture + this->setImage(ivImage); + //this->image.enableNotify(oldnotify); <- difficult to implement for SoVRMLImageTexture + // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender() + // We can safely ignore this as we are not going to render the scene graph. + } + } + else + retval = TRUE; + } + + url.enableNotify(oldNotify); + return readOK; +} + +#endif /* __COIN__ */ + /////////////////////////////////////////////////////////////// SoCallbackAction::Response ConvertFromInventor::postTexture(void* data, SoCallbackAction *, @@ -1106,7 +1420,7 @@ ConvertFromInventor::preLight(void* data, SoCallbackAction* action, #if 1 // Let's place the light to its place in scene graph instead of // old approach of global light group. SbVec3f l(dirLight->direction.getValue()); - osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 0.)); + osgLight->setPosition(osg::Vec4(-l[0], -l[1], -l[2] , 0.)); #else osg::Vec3 transVec; thisPtr->transformLight(action, dirLight->direction.getValue(), transVec); @@ -1197,7 +1511,7 @@ ConvertFromInventor::preEnvironment(void* data, SoCallbackAction* action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - OSG_DEBUG << NOTIFY_HEADER << "preLight() " + OSG_DEBUG << NOTIFY_HEADER << "preEnvironment() " << node->getTypeId().getName().getString() << std::endl; #endif @@ -1900,3 +2214,12 @@ void ConvertFromInventor::addPointCB(void* data, SoCallbackAction* action, thisPtr->numPrimitives++; thisPtr->primitiveType = osg::PrimitiveSet::POINTS; } +////////////////////////////////////////////////////////////////////////// +void ConvertFromInventor::init() +{ +#ifdef __COIN__ + SoTexture2Osg::overrideClass(); + SoTexture3Osg::overrideClass(); + SoVRMLImageTextureOsg::overrideClass(); +#endif +} diff --git a/src/osgPlugins/Inventor/ConvertFromInventor.h b/src/osgPlugins/Inventor/ConvertFromInventor.h index 6d1885663..3111361bb 100644 --- a/src/osgPlugins/Inventor/ConvertFromInventor.h +++ b/src/osgPlugins/Inventor/ConvertFromInventor.h @@ -18,6 +18,10 @@ class ConvertFromInventor ConvertFromInventor(); ~ConvertFromInventor(); + /** Initializes internal converting structures. + * The function is expected to be called after Inventor initialization. */ + static void init(); + /// Conversts from IV to OSG scene graph osg::Node* convert(SoNode* rootIVNode); diff --git a/src/osgPlugins/Inventor/ReaderWriterIV.cpp b/src/osgPlugins/Inventor/ReaderWriterIV.cpp index 3f650ac1f..c3a819d8a 100644 --- a/src/osgPlugins/Inventor/ReaderWriterIV.cpp +++ b/src/osgPlugins/Inventor/ReaderWriterIV.cpp @@ -55,6 +55,9 @@ void ReaderWriterIV::initInventor() const #ifdef __COIN__ // Disable delayed loading of VRML textures SoVRMLImageTexture::setDelayFetchURL(FALSE); + + // initialize convertor + ConvertFromInventor::init(); #endif }