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.
315 lines
9.8 KiB
C++
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());
|
|
}
|
|
|