Files
OpenSceneGraph/src/osgPlugins/Inventor/ReaderWriterIV.cpp
Robert Osfield 6d66e1abaa Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength
parameter in osg::Image.  To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU.  However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block.  The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous and handle the case or use access via the DataIerator or by row by row.  To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.
2012-01-24 14:34:02 +00:00

315 lines
9.8 KiB
C++

// OSG headers
#include <osg/Notify>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
// Inventor headers
#include <Inventor/SoDB.h>
#include <Inventor/SoInteraction.h>
#include <Inventor/nodekits/SoNodeKit.h>
#include <Inventor/errors/SoDebugError.h>
#include <Inventor/errors/SoMemoryError.h>
#include <Inventor/errors/SoReadError.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/actions/SoWriteAction.h>
#include <Inventor/actions/SoCallbackAction.h>
#ifdef __COIN__
# include <Inventor/VRMLnodes/SoVRMLImageTexture.h>
#endif
#include "ReaderWriterIV.h"
#include "ConvertFromInventor.h"
#include "ConvertToInventor.h"
// forward declarations of static functions
static void addSearchPaths(const osgDB::FilePathList *searchPaths);
static void removeSearchPaths(const osgDB::FilePathList *searchPaths);
static void errorCallback(const SoError *error, void * data);
// Register with Registry to instantiate the inventor reader.
REGISTER_OSGPLUGIN(Inventor, ReaderWriterIV)
/**
* Constructor.
* Initializes the ReaderWriterIV.
*/
ReaderWriterIV::ReaderWriterIV()
{
// Set supported extensions and options
supportsExtension("iv","Inventor format");
supportsExtension("wrl","VRML world file");
// Initialize Inventor
initInventor();
}
/**
* Initializes Open Inventor.
*/
void ReaderWriterIV::initInventor() const
{
// Initialize Inventor
SoDB::init();
SoNodeKit::init();
SoInteraction::init();
// Redirect error messages to OSG notify
SoError::setHandlerCallback(errorCallback, NULL);
SoDebugError::setHandlerCallback(errorCallback, NULL);
SoMemoryError::setHandlerCallback(errorCallback, NULL);
SoReadError::setHandlerCallback(errorCallback, NULL);
#ifdef __COIN__
// Disable delayed loading of VRML textures
SoVRMLImageTexture::setDelayFetchURL(FALSE);
// initialize convertor
ConvertFromInventor::init();
#endif
}
static void errorCallback(const SoError *error, void *data)
{
// note: Coin and SGI Inventor puts "Inventor read error..." or "Coin warning..."
// introduction string to the error message, so we do not prepend the error message
// by anything.
if (error->isOfType(SoDebugError::getClassTypeId()))
{
switch (((SoDebugError*)error)->getSeverity())
{
case SoDebugError::INFO:
OSG_INFO << error->getDebugString().getString() << std::endl;
break;
case SoDebugError::WARNING:
OSG_WARN << error->getDebugString().getString() << std::endl;
break;
case SoDebugError::ERROR:
default:
OSG_WARN << error->getDebugString().getString() << std::endl;
break;
}
}
else
{
OSG_WARN << error->getDebugString().getString() << std::endl;
}
}
/**
* Read from SoInput and convert to OSG.
* This is a method used by readNode(string,options) and readNode(istream,options).
*/
osgDB::ReaderWriter::ReadResult
ReaderWriterIV::readNodeFromSoInput(SoInput &input,
std::string &fileName, const osgDB::ReaderWriter::Options *options) const
{
// Parse options and add search paths to SoInput
const osgDB::FilePathList *searchPaths = options ? &options->getDatabasePathList() : NULL;
if (options)
addSearchPaths(searchPaths);
// Create the inventor scenegraph by reading from SoInput
SoSeparator* rootIVNode = SoDB::readAll(&input);
// Remove recently appened search paths
if (options)
removeSearchPaths(searchPaths);
// Close the file
input.closeFile();
// Perform conversion
ReadResult result;
if (rootIVNode)
{
rootIVNode->ref();
// Convert the inventor scenegraph to an osg scenegraph
ConvertFromInventor convertIV;
convertIV.preprocess(rootIVNode);
result = convertIV.convert(rootIVNode);
rootIVNode->unref();
} else
result = ReadResult::FILE_NOT_HANDLED;
// Notify
if (result.success()) {
if (fileName.length())
{
OSG_NOTICE << "osgDB::ReaderWriterIV::readNode() "
<< "File " << fileName
<< " loaded successfully." << std::endl;
}
else
{
OSG_NOTICE << "osgDB::ReaderWriterIV::readNode() "
<< "Stream loaded successfully." << std::endl;
}
} else {
if (fileName.length())
{
OSG_WARN << "osgDB::ReaderWriterIV::readNode() "
<< "Failed to load file " << fileName
<< "." << std::endl;
}
else
{
OSG_WARN << "osgDB::ReaderWriterIV::readNode() "
<< "Failed to load stream." << std::endl;
}
}
return result;
}
// Read file and convert to OSG
osgDB::ReaderWriter::ReadResult
ReaderWriterIV::readNode(const std::string& file,
const osgDB::ReaderWriter::Options* options) const
{
// Accept extension
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
// Find file
std::string fileName = osgDB::findDataFile( file, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
// Notify
OSG_NOTICE << "osgDB::ReaderWriterIV::readNode() Reading file "
<< fileName.data() << std::endl;
OSG_INFO << "osgDB::ReaderWriterIV::readNode() Inventor version: "
<< SoDB::getVersion() << std::endl;
// Open the file
SoInput input;
if (!input.openFile(fileName.data()))
{
OSG_WARN << "osgDB::ReaderWriterIV::readIVFile() "
<< "Cannot open file " << fileName << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
// Perform reading from SoInput
return readNodeFromSoInput(input, fileName, options);
}
osgDB::ReaderWriter::ReadResult
ReaderWriterIV::readNode(std::istream& fin,
const osgDB::ReaderWriter::Options* options) const
{
// Notify
OSG_NOTICE << "osgDB::ReaderWriterIV::readNode() "
"Reading from stream." << std::endl;
OSG_INFO << "osgDB::ReaderWriterIV::readNode() "
"Inventor version: " << SoDB::getVersion() << std::endl;
// Open the file
SoInput input;
// Assign istream to SoInput
// note: It seems there is no straightforward way to do that.
// SoInput accepts only FILE by setFilePointer or memory buffer
// by setBuffer. The FILE is dangerous on Windows, since it forces
// the plugin and Inventor DLL to use the same runtime library
// (otherwise there are app crashes).
// The memory buffer seems much better option here, even although
// there will not be a real streaming. However, the model data
// are usually much smaller than textures, so we should not worry
// about it and think how to stream textures instead.
// Get the data to the buffer
size_t bufSize = 126*1024; // let's make it something bellow 128KB
char *buf = (char*)malloc(bufSize);
size_t dataSize = 0;
while (!fin.eof() && fin.good()) {
fin.read(buf+dataSize, bufSize-dataSize);
dataSize += fin.gcount();
if (bufSize == dataSize) {
bufSize *= 2;
char* new_buf = (char*)realloc(buf, bufSize);
if (!new_buf)
{
free(buf);
return osgDB::ReaderWriter::ReadResult::INSUFFICIENT_MEMORY_TO_LOAD;
}
buf = new_buf;
}
}
input.setBuffer(buf, dataSize);
OSG_INFO << "osgDB::ReaderWriterIV::readNode() "
"Stream size: " << dataSize << std::endl;
// Perform reading from SoInput
osgDB::ReaderWriter::ReadResult r;
std::string fileName("");
r = readNodeFromSoInput(input, fileName, options);
// clean up and return
free(buf);
return r;
}
osgDB::ReaderWriter::WriteResult
ReaderWriterIV::writeNode(const osg::Node& node, const std::string& fileName,
const osgDB::ReaderWriter::Options* options) const
{
// accept extension
std::string ext = osgDB::getLowerCaseFileExtension(fileName);
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
bool useVRML1 = !isInventorExtension(osgDB::getFileExtension(fileName));
OSG_NOTICE << "osgDB::ReaderWriterIV::writeNode() Writing file "
<< fileName.data() << std::endl;
// Convert OSG graph to Inventor graph
ConvertToInventor osg2iv;
osg2iv.setVRML1Conversion(useVRML1);
(const_cast<osg::Node*>(&node))->accept(osg2iv);
SoNode *ivRoot = osg2iv.getIvSceneGraph();
if (ivRoot == NULL)
return WriteResult::ERROR_IN_WRITING_FILE;
ivRoot->ref();
// Change prefix according to VRML spec:
// Node names must not begin with a digit, and must not contain spaces or
// control characters, single or double quote characters, backslashes, curly braces,
// the sharp (#) character, the plus (+) character or the period character.
if (useVRML1)
SoBase::setInstancePrefix("_");
// Write Inventor graph to file
SoOutput out;
out.setHeaderString((useVRML1) ? "#VRML V1.0 ascii" : "#Inventor V2.1 ascii");
if (!out.openFile(fileName.c_str()))
return WriteResult::ERROR_IN_WRITING_FILE;
SoWriteAction wa(&out);
wa.apply(ivRoot);
ivRoot->unref();
return WriteResult::FILE_SAVED;
}
static void addSearchPaths(const osgDB::FilePathList *searchPaths)
{
for (int i=searchPaths->size()-1; i>=0; i--)
SoInput::addDirectoryFirst(searchPaths->operator[](i).c_str());
}
static void removeSearchPaths(const osgDB::FilePathList *searchPaths)
{
for (int i=0, c=searchPaths->size(); i<c; i++)
SoInput::addDirectoryFirst(searchPaths->operator[](i).c_str());
}