Files
OpenSceneGraph/src/osgPlugins/lwo/ReaderWriterLWO.cpp

367 lines
12 KiB
C++

// -*-c++-*-
/*
* Lightwave Object loader for Open Scene Graph
*
* Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
* Improved LWO2 reader is (C) 2003-2004 Marco Jez <marco.jez@poste.it>
*
* 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/
*/
#if defined(_MSC_VER)
#pragma warning( disable : 4786 )
#endif
#include <string>
#include <memory>
#include <sstream>
#include <algorithm>
#include <osg/Notify>
#include <osg/Node>
#include <osg/Group>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Texture2D>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgUtil/SmoothingVisitor>
#include <osgUtil/Tessellator>
#include <string.h>
#include "Converter.h"
#include "VertexMap.h"
#include "old_lw.h"
#include "old_Lwo2.h"
class ReaderWriterLWO : public osgDB::ReaderWriter
{
public:
ReaderWriterLWO()
{
supportsExtension("lwo","Lightwave object format");
supportsExtension("lw","Lightwave object format");
supportsExtension("geo","Lightwave geometry format");
}
virtual const char* className() const { return "Lightwave Object Reader"; }
virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
// code for setting up the database path so that internally referenced file are searched for on relative paths.
osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
local_opt->setDatabasePath(osgDB::getFilePath(fileName));
ReadResult result = readNode_LWO1(fileName,local_opt.get());
if (result.success()) return result;
if (!options || options->getOptionString() != "USE_OLD_READER") {
ReadResult result = readNode_LWO2(fileName, local_opt.get());
if (result.success()) return result;
}
return readNode_old_LWO2(fileName, local_opt.get());
}
lwosg::Converter::Options parse_options(const Options *options) const;
virtual ReadResult readNode_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
virtual ReadResult readNode_old_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
virtual ReadResult readNode_LWO1(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
protected:
};
lwosg::Converter::Options ReaderWriterLWO::parse_options(const Options *options) const
{
lwosg::Converter::Options conv_options;
if (options) {
std::istringstream iss(options->getOptionString());
std::string opt;
while (iss >> opt) {
if (opt == "COMBINE_GEODES") conv_options.combine_geodes = true;
if (opt == "FORCE_ARB_COMPRESSION") conv_options.force_arb_compression = true;
if (opt == "USE_OSGFX") conv_options.use_osgfx = true;
if (opt == "NO_LIGHTMODEL_ATTRIBUTE") conv_options.apply_light_model = false;
if (opt == "BIND_TEXTURE_MAP")
{
std::string mapname;
int unit;
if (iss >> mapname >> unit)
{
conv_options.texturemap_bindings.insert(lwosg::VertexMap_binding_map::value_type(mapname, unit));
}
}
if (opt == "MAX_TEXTURE_UNITS") {
int n;
if (iss >> n) {
conv_options.max_tex_units = n;
}
}
}
}
return conv_options;
}
// register with Registry to instantiate the above reader/writer.
REGISTER_OSGPLUGIN(lwo, ReaderWriterLWO)
osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_LWO2(const std::string &fileName, const osgDB::ReaderWriter::Options *options) const
{
lwosg::Converter::Options conv_options = parse_options(options);
lwosg::Converter converter(conv_options, options);
osg::ref_ptr<osg::Node> node = converter.convert(fileName);
if (node.valid()) {
return node.release();
}
return ReadResult::FILE_NOT_HANDLED;
}
osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_old_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const
{
std::auto_ptr<Lwo2> lwo2(new Lwo2());
if (lwo2->ReadFile(fileName))
{
osg::ref_ptr<Group> group = new osg::Group();
if (lwo2->GenerateGroup(*group)) return group.release();
}
return ReadResult::FILE_NOT_HANDLED;
}
// collect all the data relavent to a particular osg::Geometry being created.
struct GeometryCollection
{
GeometryCollection():
_numPrimitives(0),
_numPrimitivesWithTexCoords(0),
_numPoints(0),
_texturesActive(false),
_vertices(osg::Vec3Array::iterator()),
_texcoords(osg::Vec2Array::iterator()),
_coordCount(0),
_geom(0) {}
int _numPrimitives;
int _numPrimitivesWithTexCoords;
int _numPoints;
bool _texturesActive;
osg::Vec3Array::iterator _vertices;
osg::Vec2Array::iterator _texcoords;
int _coordCount;
osg::Geometry* _geom;
};
// read file and convert to OSG.
osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_LWO1(const std::string& fileName, const osgDB::ReaderWriter::Options*) const
{
lwObject* lw = lw_object_read(fileName.c_str(),osg::notify(osg::INFO));
if (!lw)
return ReadResult::FILE_NOT_HANDLED;
osg::notify(osg::INFO) << "faces " << lw->face_cnt << std::endl;
osg::notify(osg::INFO) << "materials " << lw->material_cnt << std::endl;
osg::notify(osg::INFO) << "vertices " << lw->vertex_cnt << std::endl;
typedef std::map<int,GeometryCollection> MaterialToGeometryCollectionMap;
MaterialToGeometryCollectionMap mtgcm;
// bin the indices for each material into the mtis;
int i;
for (i = 0; i < lw->face_cnt; ++i)
{
lwFace& face = lw->face[i];
if (face.index_cnt>=3)
{
GeometryCollection& gc = mtgcm[face.material];
gc._numPoints += face.index_cnt;
gc._numPrimitives += 1;
if (face.texcoord) gc._numPrimitivesWithTexCoords += 1;
}
}
MaterialToGeometryCollectionMap::iterator itr;
for(itr=mtgcm.begin(); itr!=mtgcm.end(); ++itr)
{
GeometryCollection& gc = itr->second;
if (gc._numPrimitives)
{
lwMaterial& lw_material = lw->material[itr->first];
gc._geom = new osg::Geometry;
osg::Vec3Array* vertArray = new osg::Vec3Array(gc._numPoints);
gc._vertices = vertArray->begin();
gc._geom->setVertexArray(vertArray);
// set up color.
osg::Vec4Array* colors = new osg::Vec4Array(1);
(*colors)[0].set(lw_material.r,
lw_material.g,
lw_material.b,
1.0f);
gc._geom->setColorArray(colors);
gc._geom->setColorBinding(osg::Geometry::BIND_OVERALL);
// set up texture if needed.
if (gc._numPrimitivesWithTexCoords==gc._numPrimitives)
{
if (lw_material.ctex.flags && strlen(lw_material.ctex.name)!=0)
{
osg::notify(osg::INFO) << "ctex " << lw_material.ctex.name << std::endl;
osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(lw_material.ctex.name);
if (image.valid())
{
// create state
osg::StateSet* stateset = new osg::StateSet;
// create texture
osg::Texture2D* texture = new osg::Texture2D;
texture->setImage(image.get());
// texture wrap mode
static osg::Texture::WrapMode mode[] = {
osg::Texture::CLAMP,
osg::Texture::CLAMP,
osg::Texture::REPEAT,
osg::Texture::MIRROR
};
texture->setWrap(osg::Texture::WRAP_S,
mode[lw_material.ctex.u_wrap]);
texture->setWrap(osg::Texture::WRAP_T,
mode[lw_material.ctex.v_wrap]);
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
gc._texturesActive=true;
gc._geom->setStateSet(stateset);
osg::Vec2Array* texcoordArray = new osg::Vec2Array(gc._numPoints);
gc._texcoords = texcoordArray->begin();
gc._geom->setTexCoordArray(0,texcoordArray);
}
}
}
}
}
for (i = 0; i < lw->face_cnt; ++i)
{
lwFace& face = lw->face[i];
if (face.index_cnt>=3)
{
GeometryCollection& gc = mtgcm[face.material];
osg::PrimitiveSet::Mode mode;
switch(face.index_cnt)
{
case(0):
mode = osg::PrimitiveSet::POINTS;
break;
case(1):
mode = osg::PrimitiveSet::POINTS;
break;
case(2):
mode = osg::PrimitiveSet::LINES;
break;
case(3):
mode = osg::PrimitiveSet::TRIANGLES;
break;
case(4):
mode = osg::PrimitiveSet::QUADS;
break;
default:
mode = osg::PrimitiveSet::POLYGON;
break;
}
gc._geom->addPrimitiveSet(new osg::DrawArrays(mode,gc._coordCount,face.index_cnt));
gc._coordCount += face.index_cnt;
// From the spec_low.lxt :
// "By convention, the +X direction is to the right or east, the +Y
// direction is upward, and the +Z direction is forward or north"
// However, the osg sticks to the more conventional, y to the north,
// z upwards, x is the same - rigth/east. To handle this difference
// simple exchange osg_z for lwo_y, and osg_y for lwo_z.
// add the corners in reverse order to reverse the windings, to keep the anticlockwise rotation of polys.
int j;
for(j=face.index_cnt-1;j>=0;--j)
{
(*gc._vertices++).set(lw->vertex[face.index[j]*3], lw->vertex[face.index[j]*3+2], lw->vertex[face.index[j]*3+1]);
}
if (gc._texturesActive && face.texcoord)
{
for(j=face.index_cnt-1;j>=0;--j)
{
(*gc._texcoords++).set(face.texcoord[j*2],face.texcoord[j*2+1]);
}
}
}
}
osg::Geode* geode = new osg::Geode;
osgUtil::Tessellator tessellator;
// add everthing into the Geode.
osgUtil::SmoothingVisitor smoother;
for(itr=mtgcm.begin();
itr!=mtgcm.end();
++itr)
{
GeometryCollection& gc = itr->second;
if (gc._geom)
{
tessellator.retessellatePolygons(*gc._geom);
smoother.smooth(*gc._geom);
geode->addDrawable(gc._geom);
}
}
// free
lw_object_free(lw);
return geode;
}