2.8 branch: Backport of 3ds plugin from trunk.
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
#include <osg/TexEnv>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/TexEnvCombine>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
@@ -182,18 +184,29 @@ protected:
|
||||
|
||||
bool createFileObject(const osg::Node& node, Lib3dsFile * file3ds,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;
|
||||
|
||||
/// An OSG state set with the original 3DS material attached (used to get info such as UV scaling & offset)
|
||||
struct StateSetInfo
|
||||
{
|
||||
StateSetInfo(osg::StateSet * stateset=NULL, Lib3dsMaterial * lib3dsmat=NULL) : stateset(stateset), lib3dsmat(lib3dsmat) {}
|
||||
StateSetInfo(const StateSetInfo & v) : stateset(v.stateset), lib3dsmat(v.lib3dsmat) {}
|
||||
StateSetInfo & operator=(const StateSetInfo & v) { stateset=v.stateset; lib3dsmat=v.lib3dsmat; return *this; }
|
||||
|
||||
osg::StateSet * stateset;
|
||||
Lib3dsMaterial * lib3dsmat;
|
||||
};
|
||||
|
||||
class ReaderObject
|
||||
{
|
||||
public:
|
||||
ReaderObject(const osgDB::ReaderWriter::Options* options);
|
||||
|
||||
typedef std::vector<osg::StateSet*> StateSetMap;
|
||||
typedef std::vector<StateSetInfo> StateSetMap;
|
||||
typedef std::vector<int> FaceList;
|
||||
typedef std::map<std::string,osg::StateSet*> GeoStateMap;
|
||||
|
||||
osg::Texture2D* createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy);
|
||||
osg::StateSet* createStateSet(Lib3dsMaterial *materials);
|
||||
osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix);
|
||||
StateSetInfo createStateSet(Lib3dsMaterial *materials);
|
||||
osg::Drawable* createDrawable(Lib3dsMesh *meshes,FaceList& faceList, const osg::Matrix * matrix, StateSetInfo & ssi);
|
||||
|
||||
std::string _directory;
|
||||
bool _useSmoothingGroups;
|
||||
@@ -201,15 +214,18 @@ protected:
|
||||
|
||||
// MIKEC
|
||||
osg::Node* processMesh(StateSetMap& drawStateMap,osg::Group* parent,Lib3dsMesh* mesh, const osg::Matrix * matrix);
|
||||
osg::Node* processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node);
|
||||
osg::Node* processNode(StateSetMap& drawStateMap,Lib3dsFile *f,Lib3dsNode *node);
|
||||
private:
|
||||
const osgDB::ReaderWriter::Options* options;
|
||||
bool noMatrixTransforms; ///< Should the plugin apply matrices into the mesh vertices ("old behaviour"), instead of restoring matrices ("new behaviour")?
|
||||
bool checkForEspilonIdentityMatrices;
|
||||
bool restoreMatrixTransformsNoMeshes;
|
||||
typedef std::map<unsigned int,FaceList> SmoothingFaceMap;
|
||||
void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, osg::StateSet * stateSet);
|
||||
};
|
||||
void addDrawableFromFace(osg::Geode * geode, FaceList & faceList, Lib3dsMesh * mesh, const osg::Matrix * matrix, StateSetInfo & ssi);
|
||||
|
||||
typedef std::map<std::string, osg::ref_ptr<osg::Texture2D> > TexturesMap; // Should be an unordered map (faster)
|
||||
TexturesMap texturesMap;
|
||||
};
|
||||
};
|
||||
|
||||
// now register with Registry to instantiate the above
|
||||
@@ -221,29 +237,28 @@ ReaderWriter3DS::ReaderWriter3DS()
|
||||
supportsExtension("3ds","3D Studio model format");
|
||||
//supportsOption("OutputTextureFiles","Write out the texture images to file");
|
||||
//supportsOption("flipTexture", "flip texture upside-down");
|
||||
supportsOption("extended3dsFilePaths", "Keeps long texture filenames (not 8.3) when exporting 3DS, but can lead to compatibility problems.");
|
||||
supportsOption("noMatrixTransforms", "Set the plugin to apply matrices into the mesh vertices (\"old behaviour\") instead of restoring them (\"new behaviour\"). You may use this option to avoid a few rounding errors.");
|
||||
supportsOption("checkForEspilonIdentityMatrices", "If not set, then consider \"almost identity\" matrices to be identity ones (in case of rounding errors).");
|
||||
supportsOption("restoreMatrixTransformsNoMeshes", "Makes an exception to the behaviour when 'noMatrixTransforms' is not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices.");
|
||||
setByteOrder();
|
||||
supportsOption("extended3dsFilePaths", "(Write option) Keeps long texture filenames (not 8.3) when exporting 3DS, but can lead to compatibility problems.");
|
||||
supportsOption("noMatrixTransforms", "(Read option) Set the plugin to apply matrices into the mesh vertices (\"old behaviour\") instead of restoring them (\"new behaviour\"). You may use this option to avoid a few rounding errors.");
|
||||
supportsOption("checkForEspilonIdentityMatrices", "(Read option) If not set, then consider \"almost identity\" matrices to be identity ones (in case of rounding errors).");
|
||||
supportsOption("restoreMatrixTransformsNoMeshes", "(Read option) Makes an exception to the behaviour when 'noMatrixTransforms' is not set for mesh instances. When a mesh instance has a transform on it, the reader creates a MatrixTransform above the Geode. If you don't want the hierarchy to be modified, then you can use this option to merge the transform into vertices.");
|
||||
|
||||
#if 0
|
||||
osg::notify(osg::NOTICE)<<"3DS reader sizes:"<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsBool)="<<sizeof(Lib3dsBool)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsByte)="<<sizeof(Lib3dsByte)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsWord)="<<sizeof(Lib3dsWord)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsDword)="<<sizeof(Lib3dsDword)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsIntb)="<<sizeof(Lib3dsIntb)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsIntw)="<<sizeof(Lib3dsIntw)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsIntd)="<<sizeof(Lib3dsIntd)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsFloat)="<<sizeof(Lib3dsFloat)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsDouble)="<<sizeof(Lib3dsDouble)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsVector)="<<sizeof(Lib3dsVector)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsTexel)="<<sizeof(Lib3dsTexel)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsQuat)="<<sizeof(Lib3dsQuat)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsMatrix)="<<sizeof(Lib3dsMatrix)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsRgb)="<<sizeof(Lib3dsRgb)<<std::endl;
|
||||
osg::notify(osg::NOTICE)<<" sizeof(Lib3dsRgba)="<<sizeof(Lib3dsRgba)<<std::endl;
|
||||
OSG_NOTICE<<"3DS reader sizes:"<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsBool)="<<sizeof(Lib3dsBool)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsByte)="<<sizeof(Lib3dsByte)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsWord)="<<sizeof(Lib3dsWord)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsDword)="<<sizeof(Lib3dsDword)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsIntb)="<<sizeof(Lib3dsIntb)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsIntw)="<<sizeof(Lib3dsIntw)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsIntd)="<<sizeof(Lib3dsIntd)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsFloat)="<<sizeof(Lib3dsFloat)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsDouble)="<<sizeof(Lib3dsDouble)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsVector)="<<sizeof(Lib3dsVector)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsTexel)="<<sizeof(Lib3dsTexel)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsQuat)="<<sizeof(Lib3dsQuat)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsMatrix)="<<sizeof(Lib3dsMatrix)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsRgb)="<<sizeof(Lib3dsRgb)<<std::endl;
|
||||
OSG_NOTICE<<" sizeof(Lib3dsRgba)="<<sizeof(Lib3dsRgba)<<std::endl;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -264,9 +279,9 @@ ReaderWriter3DS::ReaderObject::ReaderObject(const osgDB::ReaderWriter::Options*
|
||||
{
|
||||
if (opt == "noMatrixTransforms")
|
||||
noMatrixTransforms = true;
|
||||
if (opt == "checkForEspilonIdentityMatrices")
|
||||
else if (opt == "checkForEspilonIdentityMatrices")
|
||||
checkForEspilonIdentityMatrices = true;
|
||||
if (opt == "restoreMatrixTransformsNoMeshes")
|
||||
else if (opt == "restoreMatrixTransformsNoMeshes")
|
||||
restoreMatrixTransformsNoMeshes = true;
|
||||
}
|
||||
}
|
||||
@@ -364,7 +379,7 @@ void print(Lib3dsNode *node, int level)
|
||||
void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, FaceList & faceList,
|
||||
Lib3dsMesh * mesh,
|
||||
const osg::Matrix * matrix,
|
||||
osg::StateSet * stateSet)
|
||||
StateSetInfo & ssi)
|
||||
{
|
||||
if (_useSmoothingGroups)
|
||||
{
|
||||
@@ -384,22 +399,22 @@ void ReaderWriter3DS::ReaderObject::addDrawableFromFace(osg::Geode * geode, Face
|
||||
// to ensure the vertices on adjacent groups
|
||||
// don't get shared.
|
||||
FaceList& smoothFaceMap = sitr->second;
|
||||
osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,smoothFaceMap,matrix);
|
||||
osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,smoothFaceMap,matrix,ssi);
|
||||
if (drawable.valid())
|
||||
{
|
||||
if (stateSet)
|
||||
drawable->setStateSet(stateSet);
|
||||
if (ssi.stateset)
|
||||
drawable->setStateSet(ssi.stateset);
|
||||
geode->addDrawable(drawable.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
else // ignore smoothing groups.
|
||||
{
|
||||
osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,faceList,matrix);
|
||||
osg::ref_ptr<osg::Drawable> drawable = createDrawable(mesh,faceList,matrix,ssi);
|
||||
if (drawable.valid())
|
||||
{
|
||||
if (stateSet)
|
||||
drawable->setStateSet(stateSet);
|
||||
if (ssi.stateset)
|
||||
drawable->setStateSet(ssi.stateset);
|
||||
geode->addDrawable(drawable.get());
|
||||
}
|
||||
}
|
||||
@@ -429,8 +444,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,
|
||||
}
|
||||
if (materialFaceMap.empty() && defaultMaterialFaceList.empty())
|
||||
{
|
||||
osg::notify(osg::NOTICE)<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
|
||||
//osg::notify(osg::INFO) << "No material assigned to mesh '" << mesh->name << "'" << std::endl;
|
||||
OSG_NOTICE<<"Warning : no triangles assigned to mesh '"<<mesh->name<<"'"<< std::endl;
|
||||
//OSG_INFO << "No material assigned to mesh '" << mesh->name << "'" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -439,7 +454,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processMesh(StateSetMap& drawStateMap,
|
||||
geode->setName(mesh->name);
|
||||
if (!defaultMaterialFaceList.empty())
|
||||
{
|
||||
addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, NULL);
|
||||
StateSetInfo emptySSI;
|
||||
addDrawableFromFace(geode, defaultMaterialFaceList, mesh, matrix, emptySSI);
|
||||
}
|
||||
for(unsigned int imat=0; imat<numMaterials; ++imat)
|
||||
{
|
||||
@@ -474,7 +490,7 @@ How to cope with pivot points in 3ds (short version)
|
||||
Transform the node by the node matrix, which does the orientation about the pivot point, (and currently) transforms the object back by a translation to the PP.
|
||||
|
||||
*/
|
||||
osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,Lib3dsFile *f,Lib3dsNode *node)
|
||||
osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap& drawStateMap,Lib3dsFile *f,Lib3dsNode *node)
|
||||
{
|
||||
// Get mesh
|
||||
Lib3dsMeshInstanceNode * object = (node->type == LIB3DS_NODE_MESH_INSTANCE) ? reinterpret_cast<Lib3dsMeshInstanceNode *>(node) : NULL;
|
||||
@@ -483,13 +499,14 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L
|
||||
|
||||
// Retreive LOCAL transform
|
||||
static const osg::Matrix::value_type MATRIX_EPSILON = 1e-10;
|
||||
osg::Matrix osgNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) );
|
||||
|
||||
osg::Matrix osgWorldToNodeMatrix( copyLib3dsMatrixToOsgMatrix(node->matrix) );
|
||||
osg::Matrix osgWorldToParentNodeMatrix;
|
||||
if (node->parent)
|
||||
{
|
||||
// Matrices evaluated by lib3DS are multiplied by parents' ones
|
||||
osgNodeMatrix *= osg::Matrix::inverse( copyLib3dsMatrixToOsgMatrix(node->parent->matrix) );
|
||||
osgWorldToParentNodeMatrix = copyLib3dsMatrixToOsgMatrix(node->parent->matrix);
|
||||
}
|
||||
osg::Matrix osgNodeMatrix( osgWorldToNodeMatrix * osg::Matrix::inverse(osgWorldToParentNodeMatrix) );
|
||||
|
||||
// Test if we should create an intermediate Group (or MatrixTransform) and which matrix we should apply to the vertices
|
||||
osg::Group* group = NULL;
|
||||
@@ -501,20 +518,20 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L
|
||||
osg::Matrix meshMat;
|
||||
if (mesh)
|
||||
{
|
||||
if (!noMatrixTransforms)
|
||||
{
|
||||
if (!noMatrixTransforms)
|
||||
{
|
||||
// There can be a transform directly on a mesh instance (= as if a osg::MatrixTransform and a osg::Geode were merged together) in object->pos/rot/scl
|
||||
if (!pivoted) {
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix));
|
||||
} else {
|
||||
if (pivoted) {
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot);
|
||||
} else {
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pivoted) {
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * copyLib3dsMatrixToOsgMatrix(node->matrix);
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osg::Matrix::translate(-pivot) * osgWorldToNodeMatrix;
|
||||
} else {
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * copyLib3dsMatrixToOsgMatrix(node->matrix);
|
||||
meshMat = osg::Matrix::inverse(copyLib3dsMatrixToOsgMatrix(mesh->matrix)) * osgWorldToNodeMatrix; // ==Identity when not pivoted?
|
||||
}
|
||||
osgNodeMatrix = osg::Matrix::identity(); // Not sure it's useful, but it's harmless ;)
|
||||
}
|
||||
@@ -547,6 +564,8 @@ osg::Node* ReaderWriter3DS::ReaderObject::processNode(StateSetMap drawStateMap,L
|
||||
if (node->type == LIB3DS_NODE_MESH_INSTANCE)
|
||||
group->setName(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name);
|
||||
}
|
||||
else if (node->type == LIB3DS_NODE_MESH_INSTANCE && strlen(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name) != 0)
|
||||
group->setName(reinterpret_cast<Lib3dsMeshInstanceNode *>(node)->instance_name);
|
||||
else
|
||||
group->setName(node->name);
|
||||
|
||||
@@ -651,7 +670,7 @@ static void fileio_log_func(void *self, Lib3dsLogLevel level, int indent, const
|
||||
else if (level == LIB3DS_LOG_WARN) l = osg::NOTICE;
|
||||
else if (level == LIB3DS_LOG_INFO) l = osg::INFO;
|
||||
else if (level == LIB3DS_LOG_DEBUG) l = osg::DEBUG_INFO;
|
||||
osg::notify(l) << msg << std::endl;
|
||||
OSG_NOTIFY(l) << msg << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -703,7 +722,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::readNode(const std::string& fil
|
||||
// Do not use the lib3ds_file_open() as:
|
||||
// 1. It relies on FILE* instead of iostreams (less safe)
|
||||
// 2. It doesn't allow us to set a custom log output
|
||||
std::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
osgDB::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
if (!fin.good()) return ReadResult::ERROR_IN_READING_FILE;
|
||||
return doReadNode(fin, options, fileName);
|
||||
/*
|
||||
@@ -739,7 +758,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile
|
||||
|
||||
ReaderObject::StateSetMap drawStateMap;
|
||||
unsigned int numMaterials = f->nmaterials;
|
||||
drawStateMap.insert(drawStateMap.begin(), numMaterials, NULL); // Setup the map
|
||||
drawStateMap.insert(drawStateMap.begin(), numMaterials, StateSetInfo()); // Setup the map
|
||||
for (unsigned int imat=0; imat<numMaterials; ++imat)
|
||||
{
|
||||
Lib3dsMaterial * mat = f->materials[imat];
|
||||
@@ -769,7 +788,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile
|
||||
// Loading and re-exporting these files from 3DS produces a file with correct node structure, so perhaps these are not 100% conformant?
|
||||
if (f->nodes == NULL)
|
||||
{
|
||||
osg::notify(osg::WARN)<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl;
|
||||
OSG_WARN<<"Warning: in 3ds loader: file has no nodes, traversing by meshes instead"<< std::endl;
|
||||
traverse_nodes=true;
|
||||
}
|
||||
|
||||
@@ -803,8 +822,8 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile
|
||||
|
||||
if (osg::getNotifyLevel()>=osg::INFO)
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "Final OSG node structure looks like this:"<< endl;
|
||||
PrintVisitor pv(osg::notify(osg::NOTICE));
|
||||
OSG_INFO << "Final OSG node structure looks like this:"<< endl;
|
||||
PrintVisitor pv(osg::notify(osg::INFO));
|
||||
group->accept(pv);
|
||||
}
|
||||
|
||||
@@ -814,7 +833,7 @@ osgDB::ReaderWriter::ReadResult ReaderWriter3DS::constructFrom3dsFile(Lib3dsFile
|
||||
/**
|
||||
use matrix to pretransform geometry, or NULL to do nothing
|
||||
*/
|
||||
osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix)
|
||||
osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceList& faceList, const osg::Matrix * matrix, StateSetInfo & ssi)
|
||||
{
|
||||
osg::Geometry * geom = new osg::Geometry;
|
||||
unsigned int i;
|
||||
@@ -869,9 +888,24 @@ osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceL
|
||||
{
|
||||
osg::ref_ptr<osg::Vec2Array> osg_tcoords = new osg::Vec2Array(noVertex);
|
||||
geom->setTexCoordArray(0, osg_tcoords.get());
|
||||
|
||||
// Texture 0 parameters (only one texture supported for now)
|
||||
float scaleU(1.f), scaleV(1.f);
|
||||
float offsetU(0.f), offsetV(0.f);
|
||||
if (ssi.lib3dsmat && *(ssi.lib3dsmat->texture1_map.name)) // valid texture = name not empty
|
||||
{
|
||||
Lib3dsTextureMap & tex3ds = ssi.lib3dsmat->texture1_map;
|
||||
scaleU = tex3ds.scale[0];
|
||||
scaleV = tex3ds.scale[1];
|
||||
offsetU = tex3ds.offset[0];
|
||||
offsetV = tex3ds.offset[1];
|
||||
if (tex3ds.rotation != 0) OSG_NOTICE << "3DS texture rotation not supported yet" << std::endl;
|
||||
//TODO: tint_1, tint_2, tint_r, tint_g, tint_b
|
||||
}
|
||||
|
||||
for (i=0; i<m->nvertices; ++i)
|
||||
{
|
||||
if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0],m->texcos[i][1]);
|
||||
if (orig2NewMapping[i]>=0) (*osg_tcoords)[orig2NewMapping[i]].set(m->texcos[i][0]*scaleU + offsetU, m->texcos[i][1]*scaleV + offsetV);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,18 +992,26 @@ osg::Drawable* ReaderWriter3DS::ReaderObject::createDrawable(Lib3dsMesh *m,FaceL
|
||||
}
|
||||
|
||||
|
||||
osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparancy)
|
||||
osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *texture,const char* label,bool& transparency)
|
||||
{
|
||||
if (texture && *(texture->name))
|
||||
{
|
||||
osg::notify(osg::INFO)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
|
||||
OSG_INFO<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
|
||||
|
||||
// First try already loaded textures.
|
||||
TexturesMap::iterator itTex = texturesMap.find(texture->name);
|
||||
if (itTex != texturesMap.end()) {
|
||||
OSG_DEBUG << "Texture '" << texture->name << "' found in cache." << std::endl;
|
||||
return itTex->second.get();
|
||||
}
|
||||
|
||||
// Texture not in cache: locate and load.
|
||||
std::string fileName = osgDB::findFileInDirectory(texture->name,_directory,osgDB::CASE_INSENSITIVE);
|
||||
if (fileName.empty())
|
||||
{
|
||||
// file not found in .3ds file's directory, so we'll look in the datafile path list.
|
||||
fileName = osgDB::findDataFile(texture->name,options, osgDB::CASE_INSENSITIVE);
|
||||
osg::notify(osg::INFO)<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
|
||||
OSG_INFO<<"texture->name="<<texture->name<<", _directory="<<_directory<<std::endl;
|
||||
}
|
||||
|
||||
if (fileName.empty())
|
||||
@@ -981,30 +1023,30 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::notify(osg::WARN) << "texture '"<<texture->name<<"' not found"<< std::endl;
|
||||
OSG_WARN << "texture '"<<texture->name<<"' not found"<< std::endl;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (label) { osg::notify(osg::DEBUG_INFO) << label; }
|
||||
else { osg::notify(osg::DEBUG_INFO) << "texture name"; }
|
||||
if (label) { OSG_DEBUG << label; }
|
||||
else { OSG_DEBUG << "texture name"; }
|
||||
|
||||
osg::notify(osg::DEBUG_INFO) << " '"<<texture->name<<"'"<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " texture flag "<<texture->flags<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_DECALE "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_MIRROR "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_NEGATE "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_NO_TILE "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_SUMMED_AREA "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_TINT "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl;
|
||||
osg::notify(osg::DEBUG_INFO) << " LIB3DS_TEXTURE_RGB_TINT "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl;
|
||||
OSG_DEBUG << " '"<<texture->name<<"'"<< std::endl;
|
||||
OSG_DEBUG << " texture flag "<<texture->flags<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_DECALE "<<((texture->flags)&LIB3DS_TEXTURE_DECALE)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_MIRROR "<<((texture->flags)&LIB3DS_TEXTURE_MIRROR)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_NEGATE "<<((texture->flags)&LIB3DS_TEXTURE_NEGATE)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_NO_TILE "<<((texture->flags)&LIB3DS_TEXTURE_NO_TILE)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_SUMMED_AREA "<<((texture->flags)&LIB3DS_TEXTURE_SUMMED_AREA)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_ALPHA_SOURCE "<<((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_TINT "<<((texture->flags)&LIB3DS_TEXTURE_TINT)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_IGNORE_ALPHA "<<((texture->flags)&LIB3DS_TEXTURE_IGNORE_ALPHA)<< std::endl;
|
||||
OSG_DEBUG << " LIB3DS_TEXTURE_RGB_TINT "<<((texture->flags)&LIB3DS_TEXTURE_RGB_TINT)<< std::endl;
|
||||
|
||||
osg::ref_ptr<osg::Image> osg_image = osgDB::readRefImageFile(fileName.c_str(), options); //Absolute Path
|
||||
if (!osg_image.valid())
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "Warning: Cannot create texture "<<texture->name<< std::endl;
|
||||
OSG_NOTICE << "Warning: Cannot create texture "<<texture->name<< std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (osg_image->getFileName().empty()) // it should be done in OSG with osgDB::readRefImageFile(fileName.c_str());
|
||||
@@ -1013,7 +1055,7 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *
|
||||
osg_texture->setImage(osg_image.get());
|
||||
osg_texture->setName(texture->name);
|
||||
// does the texture support transparancy?
|
||||
transparancy = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0;
|
||||
//transparency = ((texture->flags)&LIB3DS_TEXTURE_ALPHA_SOURCE)!=0;
|
||||
|
||||
// what is the wrap mode of the texture.
|
||||
osg::Texture2D::WrapMode wm = ((texture->flags)&LIB3DS_TEXTURE_NO_TILE) ?
|
||||
@@ -1025,6 +1067,8 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *
|
||||
// bilinear.
|
||||
osg_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_NEAREST);
|
||||
|
||||
// Insert in cache map
|
||||
texturesMap.insert(TexturesMap::value_type(texture->name, osg_texture));
|
||||
return osg_texture;
|
||||
}
|
||||
else
|
||||
@@ -1032,38 +1076,42 @@ osg::Texture2D* ReaderWriter3DS::ReaderObject::createTexture(Lib3dsTextureMap *
|
||||
}
|
||||
|
||||
|
||||
osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat)
|
||||
ReaderWriter3DS::StateSetInfo ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat)
|
||||
{
|
||||
if (mat==NULL) return NULL;
|
||||
if (mat==NULL) return StateSetInfo();
|
||||
|
||||
bool textureTransparency=false;
|
||||
bool transparency = false;
|
||||
float alpha = 1.0f - mat->transparency;
|
||||
int unit = 0;
|
||||
|
||||
osg::StateSet* stateset = new osg::StateSet;
|
||||
|
||||
osg::Material* material = new osg::Material;
|
||||
|
||||
float transparency = mat->transparency;
|
||||
float alpha = 1.0f-transparency;
|
||||
|
||||
osg::Vec4 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2],alpha);
|
||||
osg::Vec4 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2],alpha);
|
||||
osg::Vec4 specular(mat->specular[0],mat->specular[1],mat->specular[2],alpha);
|
||||
osg::Vec3 ambient(mat->ambient[0],mat->ambient[1],mat->ambient[2]);
|
||||
osg::Vec3 diffuse(mat->diffuse[0],mat->diffuse[1],mat->diffuse[2]);
|
||||
osg::Vec3 specular(mat->specular[0],mat->specular[1],mat->specular[2]);
|
||||
specular *= mat->shin_strength;
|
||||
float shininess = mat->shininess*128.0f;
|
||||
|
||||
float shininess = mat->shininess;
|
||||
material->setName(mat->name);
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK,ambient);
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK,diffuse);
|
||||
material->setSpecular(osg::Material::FRONT_AND_BACK,specular);
|
||||
material->setShininess(osg::Material::FRONT_AND_BACK,shininess*128.0f);
|
||||
|
||||
stateset->setAttribute(material);
|
||||
|
||||
bool textureTransparancy=false;
|
||||
osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparancy);
|
||||
// diffuse
|
||||
osg::Texture2D* texture1_map = createTexture(&(mat->texture1_map),"texture1_map",textureTransparency);
|
||||
if (texture1_map)
|
||||
{
|
||||
stateset->setTextureAttributeAndModes(0,texture1_map,osg::StateAttribute::ON);
|
||||
stateset->setTextureAttributeAndModes(unit, texture1_map, osg::StateAttribute::ON);
|
||||
|
||||
if (!textureTransparancy)
|
||||
double factor = mat->texture1_map.percent;
|
||||
if(factor < 1.0)
|
||||
{
|
||||
osg::TexEnvCombine* texenv = new osg::TexEnvCombine();
|
||||
texenv->setCombine_RGB(osg::TexEnvCombine::MODULATE);
|
||||
texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE);
|
||||
texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS);
|
||||
texenv->setSource2_RGB(osg::TexEnvCombine::CONSTANT);
|
||||
texenv->setConstantColor(osg::Vec4(factor, factor, factor, factor));
|
||||
stateset->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON);
|
||||
}
|
||||
else
|
||||
{
|
||||
// from an email from Eric Hamil, September 30, 2003.
|
||||
// According to the 3DS spec, and other
|
||||
@@ -1082,32 +1130,55 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat
|
||||
#else
|
||||
// try alternative to avoid saturating with white
|
||||
// setting white as per OpenGL defaults.
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,alpha));
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,alpha));
|
||||
material->setSpecular(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,alpha));
|
||||
ambient.set(0.2f,0.2f,0.2f);
|
||||
diffuse.set(0.8f,0.8f,0.8f);
|
||||
specular.set(0.0f,0.0f,0.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// no longer required...
|
||||
// bool decal = false;
|
||||
//
|
||||
// // not sure exactly how to interpret what is best for .3ds
|
||||
// // but the default text env MODULATE doesn't work well, and
|
||||
// // DECAL seems to work better.
|
||||
// osg::TexEnv* texenv = new osg::TexEnv;
|
||||
// if (decal)
|
||||
// {
|
||||
// texenv->setMode(osg::TexEnv::DECAL);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// texenv->setMode(osg::TexEnv::MODULATE);
|
||||
// }
|
||||
// stateset->setTextureAttribute(0,texenv);
|
||||
unit++;
|
||||
}
|
||||
|
||||
if (transparency>0.0f || textureTransparancy)
|
||||
// opacity
|
||||
osg::Texture* opacity_map = createTexture(&(mat->opacity_map),"opacity_map", textureTransparency);
|
||||
if (opacity_map)
|
||||
{
|
||||
if(texture1_map->getImage()->isImageTranslucent())
|
||||
{
|
||||
transparency = true;
|
||||
|
||||
stateset->setTextureAttributeAndModes(unit, opacity_map, osg::StateAttribute::ON);
|
||||
|
||||
double factor = mat->opacity_map.percent;
|
||||
|
||||
osg::TexEnvCombine* texenv = new osg::TexEnvCombine();
|
||||
texenv->setCombine_Alpha(osg::TexEnvCombine::INTERPOLATE);
|
||||
texenv->setSource0_Alpha(osg::TexEnvCombine::TEXTURE);
|
||||
texenv->setSource1_Alpha(osg::TexEnvCombine::PREVIOUS);
|
||||
texenv->setSource2_Alpha(osg::TexEnvCombine::CONSTANT);
|
||||
texenv->setConstantColor(osg::Vec4(factor, factor, factor, 1.0 - factor));
|
||||
stateset->setTextureAttributeAndModes(unit, texenv, osg::StateAttribute::ON);
|
||||
|
||||
unit++;
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::notify(WARN)<<"The plugin does not support images without alpha channel for opacity"<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// material
|
||||
material->setName(mat->name);
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(ambient, alpha));
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(diffuse, alpha));
|
||||
material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(specular, alpha));
|
||||
material->setShininess(osg::Material::FRONT_AND_BACK, shininess);
|
||||
|
||||
stateset->setAttribute(material);
|
||||
|
||||
if ((alpha < 1.0f) || transparency)
|
||||
{
|
||||
//stateset->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
|
||||
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
@@ -1129,7 +1200,7 @@ osg::StateSet* ReaderWriter3DS::ReaderObject::createStateSet(Lib3dsMaterial *mat
|
||||
osg::ref_ptr<osg::Texture> reflection_map = createTexture(&(mat->reflection_map),"reflection_map",textureTransparancy);
|
||||
osg::ref_ptr<osg::Texture> reflection_mask = createTexture(&(mat->reflection_mask),"reflection_mask",textureTransparancy);
|
||||
*/
|
||||
return stateset;
|
||||
return StateSetInfo(stateset, mat);
|
||||
}
|
||||
|
||||
|
||||
@@ -1139,7 +1210,7 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod
|
||||
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
osgDB::makeDirectoryForFile(fileName.c_str());
|
||||
std::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::binary);
|
||||
osgDB::ofstream fout(fileName.c_str(), std::ios_base::out | std::ios_base::binary);
|
||||
if (!fout.good()) return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
return doWriteNode(node, fout, options, fileName);
|
||||
/*
|
||||
@@ -1160,7 +1231,7 @@ osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& nod
|
||||
|
||||
osgDB::ReaderWriter::WriteResult ReaderWriter3DS::writeNode(const osg::Node& node,std::ostream& fout,const Options* options) const
|
||||
{
|
||||
//osg::notify(osg::WARN) << "!!WARNING!! 3DS write support is incomplete" << std::endl;
|
||||
//OSG_WARN << "!!WARNING!! 3DS write support is incomplete" << std::endl;
|
||||
std::string optFileName;
|
||||
if (options)
|
||||
{
|
||||
|
||||
@@ -1,138 +1,138 @@
|
||||
#include "WriterCompareTriangle.h"
|
||||
#include <assert.h>
|
||||
|
||||
WriterCompareTriangle::WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices) : geode(geode)
|
||||
{
|
||||
cutscene(nbVertices, geode.getBoundingBox());
|
||||
}
|
||||
|
||||
bool
|
||||
WriterCompareTriangle::operator()(const std::pair<Triangle, int> & t1,
|
||||
const std::pair<Triangle, int> & t2) const
|
||||
{
|
||||
const osg::Geometry *g = geode.getDrawable( t1.second )->asGeometry();
|
||||
|
||||
const osg::Vec3Array * vecs= static_cast<const osg::Vec3Array *>(g->getVertexArray());
|
||||
const osg::BoundingBox::vec_type v1( (*vecs)[t1.first.t1] );
|
||||
|
||||
if (t1.second != t2.second)
|
||||
{
|
||||
const osg::Geometry *g = geode.getDrawable( t2.second )->asGeometry();
|
||||
vecs = static_cast<const osg::Vec3Array *>(g->getVertexArray());
|
||||
};
|
||||
const osg::BoundingBox::vec_type v2( (*vecs)[t2.first.t1] );
|
||||
int val1 = inWhichBox(v1);
|
||||
int val2 = inWhichBox(v2);
|
||||
|
||||
return (val1 < val2);
|
||||
}
|
||||
|
||||
void
|
||||
WriterCompareTriangle::setMaxMin(unsigned int & nbVerticesX,
|
||||
unsigned int & nbVerticesY,
|
||||
unsigned int & nbVerticesZ) const
|
||||
{
|
||||
static const unsigned int min = 1;
|
||||
static const unsigned int max = 5; // Number of blocks used to divide the scene (arbitrary but seems ok)
|
||||
nbVerticesX = osg::clampBetween<unsigned int>(nbVerticesX, min, max);
|
||||
nbVerticesY = osg::clampBetween<unsigned int>(nbVerticesY, min, max);
|
||||
nbVerticesZ = osg::clampBetween<unsigned int>(nbVerticesZ, min, max);
|
||||
}
|
||||
|
||||
void WriterCompareTriangle::cutscene(int nbVertices, const osg::BoundingBox & sceneBox)
|
||||
{
|
||||
osg::BoundingBox::vec_type length = sceneBox._max - sceneBox._min;
|
||||
|
||||
static const float k = 1.3f; // Arbitrary constant multiplier for density computation ("simulates" non-uniform point distributions)
|
||||
// Computes "density" of points, and thus the number of blocks to divide the mesh into
|
||||
unsigned int nbVerticesX = static_cast<unsigned int>( (nbVertices * k) / (length.z() * length.y()) );
|
||||
unsigned int nbVerticesY = static_cast<unsigned int>( (nbVertices * k) / (length.z() * length.x()) );
|
||||
unsigned int nbVerticesZ = static_cast<unsigned int>( (nbVertices * k) / (length.x() * length.y()) );
|
||||
|
||||
setMaxMin (nbVerticesX, nbVerticesY, nbVerticesZ); // This function prevent from cutting the scene in too many blocs
|
||||
|
||||
osg::notify(osg::INFO)
|
||||
<< "Cutting x by " << nbVerticesX << std::endl
|
||||
<< "Cutting y by " << nbVerticesY << std::endl
|
||||
<< "Cutting z by " << nbVerticesZ << std::endl;
|
||||
|
||||
osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; // These 3 lines set the size of a bloc in x, y and z
|
||||
osg::BoundingBox::value_type blocY = length.y() / nbVerticesY;
|
||||
osg::BoundingBox::value_type blocZ = length.z() / nbVerticesZ;
|
||||
|
||||
boxList.reserve(nbVerticesX * nbVerticesY * nbVerticesZ);
|
||||
short yinc = 1;
|
||||
short xinc = 1;
|
||||
unsigned int y = 0;
|
||||
unsigned int x = 0;
|
||||
for (unsigned int z = 0; z < nbVerticesZ; ++z)
|
||||
{
|
||||
while (x < nbVerticesX && x >= 0)
|
||||
{
|
||||
while (y < nbVerticesY && y >= 0)
|
||||
{
|
||||
osg::BoundingBox::value_type xMin = sceneBox.xMin() + x * blocX;
|
||||
if (x == 0) //to prevent from mesh with no case
|
||||
xMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type yMin = sceneBox.yMin() + y * blocY;
|
||||
if (y == 0) //to prevent from mesh with no case
|
||||
yMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type zMin = sceneBox.zMin() + z * blocZ;
|
||||
if (z == 0) //to prevent from mesh with no case
|
||||
zMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type xMax = sceneBox.xMin() + (x + 1) * blocX;
|
||||
if (x == nbVerticesX - 1) //to prevent from mesh with no case
|
||||
xMax += 10;
|
||||
|
||||
osg::BoundingBox::value_type yMax = sceneBox.yMin() + (y + 1) * blocY;
|
||||
if (y == nbVerticesY - 1) //to prevent from mesh with no case
|
||||
yMax += 10;
|
||||
|
||||
osg::BoundingBox::value_type zMax = sceneBox.zMin() + (z + 1) * blocZ;
|
||||
if (z == nbVerticesZ - 1) //to prevent from mesh with no case
|
||||
zMax += 10;
|
||||
|
||||
boxList.push_back(osg::BoundingBox(xMin, // Add a bloc to the list
|
||||
yMin,
|
||||
zMin,
|
||||
xMax,
|
||||
yMax,
|
||||
zMax));
|
||||
y += yinc;
|
||||
}
|
||||
yinc = -yinc;
|
||||
y += yinc;
|
||||
x += xinc;
|
||||
}
|
||||
xinc = -xinc;
|
||||
x += xinc;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
WriterCompareTriangle::inWhichBox(const osg::BoundingBox::value_type x,
|
||||
const osg::BoundingBox::value_type y,
|
||||
const osg::BoundingBox::value_type z) const
|
||||
{
|
||||
for (unsigned int i = 0; i < boxList.size(); ++i)
|
||||
{
|
||||
if (x >= boxList[i].xMin() &&
|
||||
x < boxList[i].xMax() &&
|
||||
y >= boxList[i].yMin() &&
|
||||
y < boxList[i].yMax() &&
|
||||
z >= boxList[i].zMin() &&
|
||||
z < boxList[i].zMax())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false && "Point is not in any blocs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const {
|
||||
return inWhichBox(point.x(), point.y(), point.z());
|
||||
}
|
||||
#include "WriterCompareTriangle.h"
|
||||
#include <assert.h>
|
||||
|
||||
WriterCompareTriangle::WriterCompareTriangle(const osg::Geode & geode, unsigned int nbVertices) : geode(geode)
|
||||
{
|
||||
cutscene(nbVertices, geode.getBoundingBox());
|
||||
}
|
||||
|
||||
bool
|
||||
WriterCompareTriangle::operator()(const std::pair<Triangle, int> & t1,
|
||||
const std::pair<Triangle, int> & t2) const
|
||||
{
|
||||
const osg::Geometry *g = geode.getDrawable( t1.second )->asGeometry();
|
||||
|
||||
const osg::Vec3Array * vecs= static_cast<const osg::Vec3Array *>(g->getVertexArray());
|
||||
const osg::BoundingBox::vec_type v1( (*vecs)[t1.first.t1] );
|
||||
|
||||
if (t1.second != t2.second)
|
||||
{
|
||||
const osg::Geometry *g = geode.getDrawable( t2.second )->asGeometry();
|
||||
vecs = static_cast<const osg::Vec3Array *>(g->getVertexArray());
|
||||
};
|
||||
const osg::BoundingBox::vec_type v2( (*vecs)[t2.first.t1] );
|
||||
int val1 = inWhichBox(v1);
|
||||
int val2 = inWhichBox(v2);
|
||||
|
||||
return (val1 < val2);
|
||||
}
|
||||
|
||||
void
|
||||
WriterCompareTriangle::setMaxMin(unsigned int & nbVerticesX,
|
||||
unsigned int & nbVerticesY,
|
||||
unsigned int & nbVerticesZ) const
|
||||
{
|
||||
static const unsigned int min = 1;
|
||||
static const unsigned int max = 5; // Number of blocks used to divide the scene (arbitrary but seems ok)
|
||||
nbVerticesX = osg::clampBetween<unsigned int>(nbVerticesX, min, max);
|
||||
nbVerticesY = osg::clampBetween<unsigned int>(nbVerticesY, min, max);
|
||||
nbVerticesZ = osg::clampBetween<unsigned int>(nbVerticesZ, min, max);
|
||||
}
|
||||
|
||||
void WriterCompareTriangle::cutscene(int nbVertices, const osg::BoundingBox & sceneBox)
|
||||
{
|
||||
osg::BoundingBox::vec_type length = sceneBox._max - sceneBox._min;
|
||||
|
||||
static const float k = 1.3f; // Arbitrary constant multiplier for density computation ("simulates" non-uniform point distributions)
|
||||
// Computes "density" of points, and thus the number of blocks to divide the mesh into
|
||||
unsigned int nbVerticesX = static_cast<unsigned int>( (nbVertices * k) / (length.z() * length.y()) );
|
||||
unsigned int nbVerticesY = static_cast<unsigned int>( (nbVertices * k) / (length.z() * length.x()) );
|
||||
unsigned int nbVerticesZ = static_cast<unsigned int>( (nbVertices * k) / (length.x() * length.y()) );
|
||||
|
||||
setMaxMin (nbVerticesX, nbVerticesY, nbVerticesZ); // This function prevent from cutting the scene in too many blocs
|
||||
|
||||
OSG_INFO
|
||||
<< "Cutting x by " << nbVerticesX << std::endl
|
||||
<< "Cutting y by " << nbVerticesY << std::endl
|
||||
<< "Cutting z by " << nbVerticesZ << std::endl;
|
||||
|
||||
osg::BoundingBox::value_type blocX = length.x() / nbVerticesX; // These 3 lines set the size of a bloc in x, y and z
|
||||
osg::BoundingBox::value_type blocY = length.y() / nbVerticesY;
|
||||
osg::BoundingBox::value_type blocZ = length.z() / nbVerticesZ;
|
||||
|
||||
boxList.reserve(nbVerticesX * nbVerticesY * nbVerticesZ);
|
||||
short yinc = 1;
|
||||
short xinc = 1;
|
||||
unsigned int y = 0;
|
||||
unsigned int x = 0;
|
||||
for (unsigned int z = 0; z < nbVerticesZ; ++z)
|
||||
{
|
||||
while (x < nbVerticesX && x >= 0)
|
||||
{
|
||||
while (y < nbVerticesY && y >= 0)
|
||||
{
|
||||
osg::BoundingBox::value_type xMin = sceneBox.xMin() + x * blocX;
|
||||
if (x == 0) //to prevent from mesh with no case
|
||||
xMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type yMin = sceneBox.yMin() + y * blocY;
|
||||
if (y == 0) //to prevent from mesh with no case
|
||||
yMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type zMin = sceneBox.zMin() + z * blocZ;
|
||||
if (z == 0) //to prevent from mesh with no case
|
||||
zMin -= 10;
|
||||
|
||||
osg::BoundingBox::value_type xMax = sceneBox.xMin() + (x + 1) * blocX;
|
||||
if (x == nbVerticesX - 1) //to prevent from mesh with no case
|
||||
xMax += 10;
|
||||
|
||||
osg::BoundingBox::value_type yMax = sceneBox.yMin() + (y + 1) * blocY;
|
||||
if (y == nbVerticesY - 1) //to prevent from mesh with no case
|
||||
yMax += 10;
|
||||
|
||||
osg::BoundingBox::value_type zMax = sceneBox.zMin() + (z + 1) * blocZ;
|
||||
if (z == nbVerticesZ - 1) //to prevent from mesh with no case
|
||||
zMax += 10;
|
||||
|
||||
boxList.push_back(osg::BoundingBox(xMin, // Add a bloc to the list
|
||||
yMin,
|
||||
zMin,
|
||||
xMax,
|
||||
yMax,
|
||||
zMax));
|
||||
y += yinc;
|
||||
}
|
||||
yinc = -yinc;
|
||||
y += yinc;
|
||||
x += xinc;
|
||||
}
|
||||
xinc = -xinc;
|
||||
x += xinc;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
WriterCompareTriangle::inWhichBox(const osg::BoundingBox::value_type x,
|
||||
const osg::BoundingBox::value_type y,
|
||||
const osg::BoundingBox::value_type z) const
|
||||
{
|
||||
for (unsigned int i = 0; i < boxList.size(); ++i)
|
||||
{
|
||||
if (x >= boxList[i].xMin() &&
|
||||
x < boxList[i].xMax() &&
|
||||
y >= boxList[i].yMin() &&
|
||||
y < boxList[i].yMax() &&
|
||||
z >= boxList[i].zMin() &&
|
||||
z < boxList[i].zMax())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false && "Point is not in any blocs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WriterCompareTriangle::inWhichBox(const osg::BoundingBox::vec_type & point) const {
|
||||
return inWhichBox(point.x(), point.y(), point.z());
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* Copyright (C) ???
|
||||
*
|
||||
* Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr,
|
||||
* Writing support added 2009 by Sukender (Benoit Neil), http://sukender.free.fr,
|
||||
* strongly inspired by the OBJ writer object by Stephan Huber
|
||||
*
|
||||
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
|
||||
@@ -13,6 +13,13 @@
|
||||
* The OSG homepage is http://www.openscenegraph.org/
|
||||
*/
|
||||
|
||||
|
||||
/// [EXPERIMENTAL] Disables animation data (and matrix transforms) for compatibility with some 3rd party apps.
|
||||
/// Animations are not read by all 3DS importers. Thus disabling them may allow some 3rd-party apps, such as Rhinoceros (tested with 4.0) to correctly import 3DS files.
|
||||
/// However, having proper hierarchy with matrix transforms will become impossible.
|
||||
///\warning This is still experimental, hence the compile flag. This should become a reader/writer option as soon as it works as intented (maybe "noMatrixTransforms" could become a read/write option?).
|
||||
#define DISABLE_3DS_ANIMATION 0 // Default = 0
|
||||
|
||||
#include <osg/io_utils>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Billboard>
|
||||
@@ -22,7 +29,6 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix)
|
||||
{
|
||||
for(int row=0; row<4; ++row)
|
||||
@@ -75,15 +81,9 @@ inline void copyOsgQuatToLib3dsQuat(float lib3ds_vector[4], const osg::Quat& osg
|
||||
lib3ds_vector[3] = static_cast<float>(-angle);
|
||||
}
|
||||
|
||||
std::string getFileName(const std::string & path)
|
||||
{
|
||||
unsigned int slashPos = path.find_last_of("/\\");
|
||||
if (slashPos == std::string::npos) return path;
|
||||
return path.substr(slashPos+1);
|
||||
}
|
||||
|
||||
|
||||
/// Checks if a filename (\b not path) is 8.3 (an empty name is never 8.3, and a path is never 8.3).
|
||||
/// Please note the '8' and '3' limitations are in \b bytes, not in characters (which is different when using UTF8).
|
||||
bool is83(const std::string & s)
|
||||
{
|
||||
// 012345678901
|
||||
@@ -91,7 +91,7 @@ bool is83(const std::string & s)
|
||||
if (s.find_first_of("/\\") != std::string::npos) return false; // It should not be a path, but a filename
|
||||
unsigned int len = s.length();
|
||||
if (len > 12 || len == 0) return false;
|
||||
unsigned int pointPos = s.rfind('.');
|
||||
size_t pointPos = s.rfind('.');
|
||||
if (pointPos == std::string::npos) return len <= 8; // Without point
|
||||
// With point
|
||||
if (pointPos > 8) return false;
|
||||
@@ -99,25 +99,138 @@ bool is83(const std::string & s)
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max).
|
||||
bool is3DSpath(const std::string & s, bool extendedFilePaths)
|
||||
{
|
||||
unsigned int len = s.length();
|
||||
if (len >= 64 || len == 0) return false;
|
||||
if (extendedFilePaths) return true; // Extended paths are simply those that fits the 64 bytes buffer!
|
||||
|
||||
// For each subdirectory
|
||||
unsigned int tokenLen;
|
||||
for (unsigned int tokenBegin=0, tokenEnd=0; tokenEnd == std::string::npos; tokenBegin = tokenEnd+1)
|
||||
{
|
||||
tokenEnd = s.find_first_of("/\\", tokenBegin);
|
||||
if (tokenEnd != std::string::npos) tokenLen = tokenEnd-tokenBegin-1; // -1 to avoid reading the separator
|
||||
else tokenLen = len-tokenBegin;
|
||||
if ( tokenLen>0 && !is83(s.substr(tokenBegin, tokenLen)) ) return false;
|
||||
}
|
||||
return true;
|
||||
inline std::string::size_type maxNameLen(bool extendedFilePaths, bool isNodeName) {
|
||||
if (extendedFilePaths) return 63;
|
||||
return isNodeName ? 8 : (8+1+3);
|
||||
}
|
||||
|
||||
/// Tests if the given string is a path supported by 3DS format (8.3, 63 chars max, non empty).
|
||||
inline bool is3DSName(const std::string & s, bool extendedFilePaths, bool isNodeName)
|
||||
{
|
||||
unsigned int len = s.length();
|
||||
if (len > maxNameLen(extendedFilePaths, isNodeName) || len == 0) return false;
|
||||
// Extended paths are simply those that fits the 64 bytes buffer.
|
||||
if (extendedFilePaths) return true;
|
||||
// Short paths simply must have no subdirectory.
|
||||
return is83(s);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// **************************************************************************
|
||||
// The following block of code was taken from the osgDB 2.9.x trunk code
|
||||
// (FileNameUtils) so that the 3ds plugin will be completely self-contained
|
||||
// in this release.
|
||||
|
||||
static const char * const PATH_SEPARATORS = "/\\";
|
||||
static unsigned int PATH_SEPARATORS_LEN = 2;
|
||||
|
||||
/** Helper to iterate over elements of a path (including Windows' root, if any). **/
|
||||
class PathIterator {
|
||||
public:
|
||||
PathIterator(const std::string & v);
|
||||
bool valid() const { return start!=end; }
|
||||
PathIterator & operator++();
|
||||
std::string operator*();
|
||||
|
||||
protected:
|
||||
std::string::const_iterator end; ///< End of path string
|
||||
std::string::const_iterator start; ///< Points to the first char of an element, or ==end() if no more
|
||||
std::string::const_iterator stop; ///< Points to the separator after 'start', or ==end()
|
||||
|
||||
/// Iterate until 'it' points to something different from a separator
|
||||
std::string::const_iterator skipSeparators(std::string::const_iterator it);
|
||||
std::string::const_iterator next(std::string::const_iterator it);
|
||||
};
|
||||
|
||||
PathIterator::PathIterator(const std::string & v) : end(v.end()), start(v.begin()), stop(v.begin()) { operator++(); }
|
||||
PathIterator & PathIterator::operator++()
|
||||
{
|
||||
if (!valid()) return *this;
|
||||
start = skipSeparators(stop);
|
||||
if (start != end) stop = next(start);
|
||||
return *this;
|
||||
}
|
||||
std::string PathIterator::operator*()
|
||||
{
|
||||
if (!valid()) return std::string();
|
||||
return std::string(start, stop);
|
||||
}
|
||||
|
||||
std::string::const_iterator PathIterator::skipSeparators(std::string::const_iterator it)
|
||||
{
|
||||
for (; it!=end && std::find_first_of(it, it+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != it+1; ++it) {}
|
||||
return it;
|
||||
}
|
||||
|
||||
std::string::const_iterator PathIterator::next(std::string::const_iterator it)
|
||||
{
|
||||
return std::find_first_of(it, end, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN);
|
||||
}
|
||||
|
||||
static std::string getPathRoot(const std::string& path) {
|
||||
// Test for unix root
|
||||
if (path.empty()) return "";
|
||||
if (path[0] == '/') return "/";
|
||||
// Now test for Windows root
|
||||
if (path.length()<2) return "";
|
||||
if (path[1] == ':') return path.substr(0, 2); // We should check that path[0] is a letter, but as ':' is invalid in paths in other cases, that's not a problem.
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string getPathRelative(const std::string& from, const std::string& to)
|
||||
{
|
||||
// This implementation is not 100% robust, and should be replaced with C++0x "std::path" as soon as possible.
|
||||
|
||||
// Definition: an "element" is a part between slashes. Ex: "/a/b" has two elements ("a" and "b").
|
||||
// Algorithm:
|
||||
// 1. If paths are neither both absolute nor both relative, then we cannot do anything (we need to make them absolute, but need additionnal info on how to make it). Return.
|
||||
// 2. If both paths are absolute and root isn't the same (for Windows only, as roots are of the type "C:", "D:"), then the operation is impossible. Return.
|
||||
// 3. Iterate over two paths elements until elements are equal
|
||||
// 4. For each remaining element in "from", add ".." to result
|
||||
// 5. For each remaining element in "to", add this element to result
|
||||
|
||||
// 1 & 2
|
||||
const std::string root = getPathRoot(from);
|
||||
if (root != getPathRoot(to)) {
|
||||
OSG_INFO << "Cannot relativise paths. From=" << from << ", To=" << to << ". Returning 'to' unchanged." << std::endl;
|
||||
//return to;
|
||||
return osgDB::getSimpleFileName(to);
|
||||
}
|
||||
|
||||
// 3
|
||||
PathIterator itFrom(from), itTo(to);
|
||||
// Iterators may point to Windows roots. As we tested they are equal, there is no need to ++itFrom and ++itTo.
|
||||
// However, if we got an Unix root, we must add it to the result.
|
||||
std::string res(root == "/" ? "/" : "");
|
||||
for(; itFrom.valid() && itTo.valid() && *itFrom==*itTo; ++itFrom, ++itTo) {}
|
||||
|
||||
// 4
|
||||
for(; itFrom.valid(); ++itFrom) res += "../";
|
||||
|
||||
// 5
|
||||
for(; itTo.valid(); ++itTo) res += *itTo + "/";
|
||||
|
||||
// Remove trailing slash before returning
|
||||
if (!res.empty() && std::find_first_of(res.rbegin(), res.rbegin()+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != res.rbegin()+1)
|
||||
{
|
||||
return res.substr(0, res.length()-1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// strip all extensions from the filename.
|
||||
static std::string getNameLessAllExtensions(const std::string& fileName)
|
||||
{
|
||||
// Finds start serach position: from last slash, or the begining of the string if none found
|
||||
std::string::size_type startPos = fileName.find_last_of(PATH_SEPARATORS); // Finds forward slash *or* back slash
|
||||
if (startPos == std::string::npos) startPos = 0;
|
||||
std::string::size_type dot = fileName.find_first_of('.', startPos); // Finds *FIRST* dot from start pos
|
||||
if (dot==std::string::npos) return fileName;
|
||||
return std::string(fileName.begin(),fileName.begin()+dot);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// **************************************************************************
|
||||
|
||||
// Use namespace qualification to avoid static-link symbol collitions
|
||||
// from multiply defined symbols.
|
||||
@@ -166,7 +279,7 @@ public:
|
||||
triangle.t2 = i2;
|
||||
triangle.t3 = i3;
|
||||
triangle.material = _material;
|
||||
_listTriangles.push_back(std::pair<Triangle, unsigned int>(triangle, _drawable_n));
|
||||
_listTriangles.push_back(ListTriangle::value_type(triangle, _drawable_n));
|
||||
}
|
||||
virtual void begin(GLenum mode)
|
||||
{
|
||||
@@ -355,11 +468,13 @@ void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
|
||||
case(GL_LINE_LOOP):
|
||||
//break;
|
||||
default:
|
||||
osg::notify(osg::WARN) << "3DS WriterNodeVisitor: can't handle mode " << mode << std::endl;
|
||||
OSG_WARN << "3DS WriterNodeVisitor: can't handle mode " << mode << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index) :
|
||||
index(index),
|
||||
diffuse(1,1,1,1),
|
||||
@@ -370,7 +485,7 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg
|
||||
double_sided(false),
|
||||
image(NULL),
|
||||
texture_transparency(false),
|
||||
texture_no_tile(false)
|
||||
texture_no_tile(true) // matches lib3ds_material.cpp initialize_texture_map(..) default flag setting
|
||||
{
|
||||
//static unsigned int s_objmaterial_id = 0;
|
||||
//++s_objmaterial_id;
|
||||
@@ -380,9 +495,14 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg
|
||||
diffuse = mat->getDiffuse(osg::Material::FRONT);
|
||||
ambient = mat->getAmbient(osg::Material::FRONT);
|
||||
specular = mat->getSpecular(osg::Material::FRONT);
|
||||
shininess = mat->getShininess(osg::Material::FRONT);
|
||||
shininess = mat->getShininess(osg::Material::FRONT) / 128.f;
|
||||
// OpenGL shininess = pow(2, 10.0*mat->shininess); (As in lib3ds example)
|
||||
// => mat->shininess = log.2( OpenGL shininess ) /10 (if values are >0)
|
||||
// => mat->shininess = log( OpenGL shininess ) / log(2) /10
|
||||
//shininess = mat->getShininess(osg::Material::FRONT) <= 0 ? 0 : log( mat->getShininess(osg::Material::FRONT) ) / log(2.f) / 10.f;
|
||||
|
||||
transparency = 1-diffuse.w();
|
||||
name = writerNodeVisitor.getUniqueName(mat->getName(),"mat");
|
||||
name = writerNodeVisitor.getUniqueName(mat->getName(),true,"mat");
|
||||
osg::StateAttribute * attribute = stateset->getAttribute(osg::StateAttribute::CULLFACE);
|
||||
if (!attribute)
|
||||
{
|
||||
@@ -395,13 +515,13 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg
|
||||
if (mode == osg::CullFace::BACK) double_sided = false;
|
||||
else if (mode == osg::CullFace::FRONT)
|
||||
{
|
||||
osg::notify(osg::WARN) << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl;
|
||||
OSG_WARN << "3DS Writer: Reversed face (culled FRONT) not supported yet." << std::endl;
|
||||
double_sided = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(mode == osg::CullFace::FRONT_AND_BACK);
|
||||
osg::notify(osg::WARN) << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl;
|
||||
OSG_WARN << "3DS Writer: Invisible face (culled FRONT_AND_BACK) not supported yet." << std::endl;
|
||||
double_sided = false;
|
||||
}
|
||||
}
|
||||
@@ -412,7 +532,8 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg
|
||||
if(img)
|
||||
{
|
||||
texture_transparency = (stateset->getMode(GL_BLEND) == osg::StateAttribute::ON);
|
||||
texture_no_tile = (tex->getWrap(osg::Texture2D::WRAP_S) == osg::Texture2D::CLAMP);
|
||||
osg::Texture::WrapMode wrapS = tex->getWrap(osg::Texture2D::WRAP_S);
|
||||
texture_no_tile = !(wrapS == osg::Texture2D::REPEAT || wrapS == osg::Texture2D::MIRROR);
|
||||
image = img;
|
||||
}
|
||||
}
|
||||
@@ -426,49 +547,6 @@ WriterNodeVisitor::Material::Material(WriterNodeVisitor & writerNodeVisitor, osg
|
||||
}
|
||||
|
||||
|
||||
// If 'to' is in a subdirectory of 'from' then this function returns the
|
||||
// subpath. Otherwise it just returns the file name.
|
||||
// (Same as in FBX plugin)
|
||||
std::string getPathRelative(const std::string& from/*directory*/,
|
||||
const std::string& to/*file path*/)
|
||||
{
|
||||
|
||||
std::string::size_type slash = to.find_last_of('/');
|
||||
std::string::size_type backslash = to.find_last_of('\\');
|
||||
if (slash == std::string::npos)
|
||||
{
|
||||
if (backslash == std::string::npos) return to;
|
||||
slash = backslash;
|
||||
}
|
||||
else if (backslash != std::string::npos && backslash > slash)
|
||||
{
|
||||
slash = backslash;
|
||||
}
|
||||
|
||||
if (from.empty() || from.length() > to.length())
|
||||
return osgDB::getSimpleFileName(to);
|
||||
|
||||
std::string::const_iterator itTo = to.begin();
|
||||
for (std::string::const_iterator itFrom = from.begin();
|
||||
itFrom != from.end(); ++itFrom, ++itTo)
|
||||
{
|
||||
char a = tolower(*itFrom), b = tolower(*itTo);
|
||||
if (a == '\\') a = '/';
|
||||
if (b == '\\') b = '/';
|
||||
if (a != b || itTo == to.begin() + slash + 1)
|
||||
{
|
||||
return osgDB::getSimpleFileName(to);
|
||||
}
|
||||
}
|
||||
|
||||
while (itTo != to.end() && (*itTo == '\\' || *itTo == '/'))
|
||||
{
|
||||
++itTo;
|
||||
}
|
||||
|
||||
return std::string(itTo, to.end());
|
||||
}
|
||||
|
||||
/// Converts an extension to a 3-letters long one equivalent.
|
||||
std::string convertExt(const std::string & path, bool extendedFilePaths)
|
||||
{
|
||||
@@ -488,12 +566,12 @@ WriterNodeVisitor::WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & f
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_succeeded(true),
|
||||
_srcDirectory(srcDirectory),
|
||||
file3ds(file3ds),
|
||||
_file3ds(file3ds),
|
||||
_currentStateSet(new osg::StateSet()),
|
||||
_lastMaterialIndex(0),
|
||||
_lastMeshIndex(0),
|
||||
_cur3dsNode(NULL),
|
||||
options(options),
|
||||
_options(options),
|
||||
_imageCount(0),
|
||||
_extendedFilePaths(false)
|
||||
{
|
||||
@@ -516,7 +594,7 @@ WriterNodeVisitor::WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & f
|
||||
void WriterNodeVisitor::writeMaterials()
|
||||
{
|
||||
unsigned int nbMat = _materialMap.size();
|
||||
lib3ds_file_reserve_materials(file3ds, nbMat, 1);
|
||||
lib3ds_file_reserve_materials(_file3ds, nbMat, 1);
|
||||
// Ugly thing: it seems lib3ds_file_insert_material() doesn't support insertion in a random order (else materials are not assigned the right way)
|
||||
for (unsigned int iMat=0; iMat<nbMat; ++iMat)
|
||||
{
|
||||
@@ -528,7 +606,7 @@ void WriterNodeVisitor::writeMaterials()
|
||||
found = true;
|
||||
|
||||
assert(mat.index>=0 && mat.index < static_cast<int>(_materialMap.size()));
|
||||
Lib3dsMaterial * mat3ds = lib3ds_material_new(getFileName(mat.name).c_str());
|
||||
Lib3dsMaterial * mat3ds = lib3ds_material_new(osgDB::getSimpleFileName(mat.name).c_str());
|
||||
copyOsgColorToLib3dsColor(mat3ds->ambient, mat.ambient);
|
||||
copyOsgColorToLib3dsColor(mat3ds->diffuse, mat.diffuse);
|
||||
copyOsgColorToLib3dsColor(mat3ds->specular, mat.specular);
|
||||
@@ -537,137 +615,223 @@ void WriterNodeVisitor::writeMaterials()
|
||||
mat3ds->two_sided = mat.double_sided ? 1 : 0;
|
||||
if (mat.image)
|
||||
{
|
||||
Lib3dsTextureMap & tex = mat3ds->texture1_map;
|
||||
std::string path;
|
||||
if(mat.image->getFileName().empty())
|
||||
ImageSet::const_iterator itImage( _imageSet.find(mat.image.get()) );
|
||||
if (itImage != _imageSet.end())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Image_" << _imageCount++ << ".rgb";
|
||||
path = oss.str();
|
||||
// Image has been already used
|
||||
path = itImage->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = getPathRelative(_srcDirectory, mat.image->getFileName());
|
||||
}
|
||||
path = convertExt(path, _extendedFilePaths);
|
||||
// First time we 'see' this image
|
||||
if (mat.image->getFileName().empty())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Image_" << _imageCount++ << ".rgb";
|
||||
path = oss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
path = getPathRelative(_srcDirectory, mat.image->getFileName());
|
||||
}
|
||||
path = convertExt(path, _extendedFilePaths);
|
||||
path = getUniqueName(path, false, "");
|
||||
|
||||
if(!is3DSpath(path, _extendedFilePaths))
|
||||
{
|
||||
path = getUniqueName(path, "", true);
|
||||
//path = osgDB::getSimpleFileName(path);
|
||||
// Write
|
||||
const std::string fullPath( osgDB::concatPaths(_directory, path) );
|
||||
osgDB::makeDirectoryForFile(fullPath);
|
||||
osgDB::writeImageFile(*(mat.image), fullPath, _options);
|
||||
|
||||
// Insert in map
|
||||
_imageSet.insert(ImageSet::value_type(mat.image.get(), path));
|
||||
}
|
||||
|
||||
Lib3dsTextureMap & tex = mat3ds->texture1_map;
|
||||
strcpy(tex.name, path.c_str());
|
||||
path = osgDB::concatPaths(_directory, path);
|
||||
osgDB::makeDirectoryForFile(path);
|
||||
|
||||
//if (mat.image->valid()) osgDB::writeImageFile(*(mat.image), path);
|
||||
if(_imageSet.find(mat.image.get()) == _imageSet.end())
|
||||
{
|
||||
_imageSet.insert(mat.image.get());
|
||||
osgDB::writeImageFile(*(mat.image), path);
|
||||
}
|
||||
// Here we don't assume anything about initial flags state (actually it is set to LIB3DS_TEXTURE_NO_TILE by lib3DS, but this is subject to change)
|
||||
if (mat.texture_transparency) tex.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE;
|
||||
else tex.flags &= ~LIB3DS_TEXTURE_ALPHA_SOURCE;
|
||||
if (mat.texture_no_tile) tex.flags |= LIB3DS_TEXTURE_NO_TILE;
|
||||
else tex.flags &= ~LIB3DS_TEXTURE_NO_TILE;
|
||||
}
|
||||
if (!succeeded())
|
||||
return;
|
||||
lib3ds_file_insert_material(file3ds, mat3ds, itr->second.index);
|
||||
lib3ds_file_insert_material(_file3ds, mat3ds, itr->second.index);
|
||||
break; // Ugly thing (3)
|
||||
}
|
||||
assert(found); // Ugly thing (4) - Implementation error if !found
|
||||
}
|
||||
}
|
||||
|
||||
/// Truncates an UTF8 string so that it does not takes more than a given \b bytes amount (\b excluding the potential NULL end character).
|
||||
/// The function assumes the UTF8 string is valid.
|
||||
///\return A valid UTF8 string which size is less or equal to \c byteLimit.
|
||||
// May be moved in osgDB/ConvertUTF?
|
||||
std::string utf8TruncateBytes(const std::string & s, std::string::size_type byteLimit) {
|
||||
// Untruncated strings
|
||||
if (s.size() <= byteLimit) return s;
|
||||
|
||||
std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, const std::string & _defaultPrefix, bool nameIsPath)
|
||||
// Truncated strings
|
||||
std::string::const_iterator it=s.begin(), itEnd=s.begin()+byteLimit;
|
||||
std::string::const_iterator itStop=it;
|
||||
// Note: itEnd is < s.end(), so that we can always write "it+1"
|
||||
for(; it!=itEnd; ++it) {
|
||||
unsigned char c = static_cast<unsigned char>(*it);
|
||||
if ((c & 0x80) == 0) itStop=it+1; // 7 bits ANSI. itStop must then point after that character.
|
||||
else if ((c & 0x40) != 0) itStop=it; // UTF8 sequence start: this is also past-the-end for the previous character (ANSI or UTF8)
|
||||
}
|
||||
return std::string(s.begin(), itStop);
|
||||
}
|
||||
|
||||
#ifdef OSG_USE_UTF8_FILENAME
|
||||
# define truncateFilenameBytes(str, size) utf8TruncateBytes(str, size)
|
||||
#else
|
||||
# define truncateFilenameBytes(str, size) std::string(str, 0, size)
|
||||
#endif
|
||||
|
||||
std::string WriterNodeVisitor::getUniqueName(const std::string& _defaultValue, bool isNodeName, const std::string & _defaultPrefix, int currentPrefixLen)
|
||||
{
|
||||
static const unsigned int MAX_PREFIX_LEGNTH = 4; // Arbitrarily defined to 4 chars
|
||||
assert(_defaultPrefix.length()<=MAX_PREFIX_LEGNTH); // Default prefix is too long (implementation error)
|
||||
//const unsigned int MAX_LENGTH = maxNameLen(_extendedFilePaths);
|
||||
const unsigned int MAX_PREFIX_LENGTH = _extendedFilePaths ? 52 : 6; // Arbitrarily defined for short names, kept enough room for displaying UINT_MAX (10 characters) for long names.
|
||||
assert(_defaultPrefix.length()<=4); // Default prefix is too long (implementation error)
|
||||
const std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix);
|
||||
if (currentPrefixLen<0) currentPrefixLen = osg::maximum(_defaultPrefix.length(), _defaultValue.length());
|
||||
currentPrefixLen = osg::clampBelow(currentPrefixLen, static_cast<int>(MAX_PREFIX_LENGTH));
|
||||
|
||||
// Tests if default name is valid and unique
|
||||
bool defaultIs83 = is83(_defaultValue);
|
||||
bool defaultIsValid = nameIsPath ? is3DSpath(_defaultValue, _extendedFilePaths) : defaultIs83;
|
||||
if (defaultIsValid && _nameMap.find(_defaultValue) == _nameMap.end())
|
||||
{
|
||||
_nameMap.insert(_defaultValue);
|
||||
return _defaultValue;
|
||||
}
|
||||
|
||||
// Handling of paths is not well done yet. Defaulting to something very simple.
|
||||
// We should actually ensure each component is 8 chars long, and final filename is 8.3, and total is <64 chars, or simply ensure total length for extended 3DS paths.
|
||||
std::string defaultValue(nameIsPath ? osgDB::getSimpleFileName(_defaultValue) : _defaultValue);
|
||||
std::string ext(nameIsPath ? osgDB::getFileExtensionIncludingDot(_defaultValue).substr(0, std::min<unsigned int>(_defaultValue.size(), 4)) : ""); // 4 chars = dot + 3 chars
|
||||
if (ext == ".") ext = "";
|
||||
|
||||
std::string defaultPrefix(_defaultPrefix.empty() ? "_" : _defaultPrefix);
|
||||
NameMap & nameMap = isNodeName ? _nodeNameMap : _imageNameMap;
|
||||
PrefixMap & prefixMap = isNodeName ? _nodePrefixMap : _imagePrefixMap;
|
||||
|
||||
// Handling of paths is simple. Algorithm:
|
||||
// - For short names, subdirectories are simply forbidden. Use the simple file name.
|
||||
// - Else, the whole (relative) path must simply be <64 chars.
|
||||
// After this, begin enumeration.
|
||||
std::string parentPath, filename, ext, namePrefix;
|
||||
unsigned int max_val = 0;
|
||||
std::string truncDefaultValue = "";
|
||||
for (unsigned int i = 0; i < std::min<unsigned int>(defaultValue.size(), MAX_PREFIX_LEGNTH); ++i)
|
||||
|
||||
// TODO Move the two parts of this giant if/else into two separate functions for better readability.
|
||||
if (_extendedFilePaths)
|
||||
{
|
||||
if (defaultValue[i] == '.')
|
||||
// Tests if default name is valid and unique
|
||||
if (is3DSName(_defaultValue, _extendedFilePaths, isNodeName))
|
||||
{
|
||||
truncDefaultValue = defaultValue.substr(0, i);
|
||||
break;
|
||||
std::pair<NameMap::iterator, bool> insertion( nameMap.insert(_defaultValue) );
|
||||
if (insertion.second) return _defaultValue; // Return if element is newly inserted in the map (else there is a naming collision)
|
||||
}
|
||||
}
|
||||
if (truncDefaultValue.empty())
|
||||
truncDefaultValue = defaultValue.substr(0, std::min<unsigned int>(defaultValue.size(), MAX_PREFIX_LEGNTH));
|
||||
assert(truncDefaultValue.size() <= MAX_PREFIX_LEGNTH);
|
||||
std::map<std::string, unsigned int>::iterator pairPrefix;
|
||||
|
||||
// TODO - Handle the case of extended 3DS paths and allow more than 8 chars
|
||||
defaultIs83 = is83(truncDefaultValue);
|
||||
if (defaultIs83)
|
||||
{
|
||||
max_val = static_cast<unsigned int>(pow(10., 8. - truncDefaultValue.length())) -1;
|
||||
pairPrefix = _mapPrefix.find(truncDefaultValue);
|
||||
}
|
||||
// Simply separate name and last extension
|
||||
filename = osgDB::getNameLessExtension(osgDB::getSimpleFileName(_defaultValue));
|
||||
if (!isNodeName)
|
||||
{
|
||||
ext = osgDB::getFileExtensionIncludingDot(_defaultValue);
|
||||
if (ext == ".") ext = "";
|
||||
}
|
||||
|
||||
if (defaultIs83 && (pairPrefix == _mapPrefix.end() || pairPrefix->second <= max_val))
|
||||
{
|
||||
defaultPrefix = truncDefaultValue;
|
||||
// Compute parent path
|
||||
// Pre-condition: paths are supposed to be relative.
|
||||
// If full path is too long (>MAX_PREFIX_LENGTH), cut path to let enough space for simple file name.
|
||||
// Do not cut in the middle of a name, but at path separators.
|
||||
parentPath = osgDB::getFilePath(_defaultValue);
|
||||
if (_defaultValue.length() >MAX_PREFIX_LENGTH) // Not parentPath but _defaultValue!
|
||||
{
|
||||
// Nodes names: keep last directories (used only for the root name, generally named after the full file path)
|
||||
// Images names: keep first directories (for images)
|
||||
if (isNodeName) std::reverse(parentPath.begin(), parentPath.end());
|
||||
unsigned lenToDelete(filename.length() + ext.length() + 1);
|
||||
lenToDelete = osg::clampBelow(lenToDelete, MAX_PREFIX_LENGTH);
|
||||
parentPath = truncateFilenameBytes(parentPath, MAX_PREFIX_LENGTH - lenToDelete); // +1 for the path separator
|
||||
std::string::size_type separator = parentPath.find_last_of("/\\");
|
||||
if (separator != std::string::npos) parentPath = parentPath.substr(0, separator);
|
||||
if (isNodeName) std::reverse(parentPath.begin(), parentPath.end());
|
||||
}
|
||||
|
||||
// Assert "MAX_PREFIX_LENGTH - parent path length - extension length -1" is >=0 and truncate name to this length to get our new prefix.
|
||||
assert(parentPath.length() + ext.length() <= MAX_PREFIX_LENGTH);
|
||||
const unsigned int len = MAX_PREFIX_LENGTH - (parentPath.length() + ext.length() +1);
|
||||
namePrefix = truncateFilenameBytes(filename, len);
|
||||
if (namePrefix.empty()) namePrefix = defaultPrefix;
|
||||
|
||||
// Truncate the filename to get our new prefix
|
||||
namePrefix = truncateFilenameBytes(filename, currentPrefixLen);
|
||||
|
||||
// Enough space has been reserved for UINT_MAX values
|
||||
max_val = UINT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
max_val = static_cast<unsigned int>(pow(10., 8. - defaultPrefix.length())) -1;
|
||||
pairPrefix = _mapPrefix.find(defaultPrefix);
|
||||
}
|
||||
// Get last extension, and make filename have no extension
|
||||
filename = getNameLessAllExtensions(osgDB::getSimpleFileName(_defaultValue));
|
||||
if (!isNodeName)
|
||||
{
|
||||
ext = truncateFilenameBytes(osgDB::getFileExtensionIncludingDot(_defaultValue), 4); // 4 chars = dot + 3 chars
|
||||
if (ext == ".") ext = "";
|
||||
}
|
||||
|
||||
unsigned int searchStart = 0;
|
||||
if (pairPrefix != _mapPrefix.end())
|
||||
// Tests if STRIPPED default name is valid and unique
|
||||
const std::string strippedName( filename + ext );
|
||||
if (is3DSName(strippedName, _extendedFilePaths, isNodeName))
|
||||
{
|
||||
std::pair<NameMap::iterator, bool> insertion( nameMap.insert(strippedName) );
|
||||
if (insertion.second) return strippedName; // Return if element is newly inserted in the map (else there is a naming collision)
|
||||
}
|
||||
|
||||
namePrefix = filename;
|
||||
if (namePrefix.empty()) namePrefix = defaultPrefix;
|
||||
// Truncate the filename to get our new prefix
|
||||
namePrefix = truncateFilenameBytes(namePrefix, currentPrefixLen);
|
||||
|
||||
// Compute the maximum enumeration value
|
||||
max_val = static_cast<unsigned int>(pow(10., 8. - namePrefix.length())) -1;
|
||||
}
|
||||
assert(namePrefix.size() <= MAX_PREFIX_LENGTH);
|
||||
|
||||
// Find the current enumeration value (searchStart)
|
||||
unsigned int searchStart(0);
|
||||
PrefixMap::iterator pairPrefix( prefixMap.find(namePrefix) );
|
||||
if (pairPrefix != prefixMap.end())
|
||||
{
|
||||
searchStart = pairPrefix->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if truncated name is ok
|
||||
const std::string res( osgDB::concatPaths(parentPath, namePrefix + ext) );
|
||||
if (nameMap.find(res) == nameMap.end()) {
|
||||
prefixMap.insert(std::pair<std::string, unsigned int>(namePrefix, 0));
|
||||
nameMap.insert(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for a free value
|
||||
for(unsigned int i = searchStart; i <= max_val; ++i)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << defaultPrefix << i;
|
||||
const std::string & res = ss.str();
|
||||
if (_nameMap.find(res) == _nameMap.end())
|
||||
ss << namePrefix << i;
|
||||
const std::string res( osgDB::concatPaths(parentPath, ss.str() + ext) );
|
||||
if (nameMap.find(res) == nameMap.end())
|
||||
{
|
||||
if (pairPrefix != _mapPrefix.end())
|
||||
if (pairPrefix != prefixMap.end())
|
||||
{
|
||||
pairPrefix->second = i + 1;
|
||||
} else
|
||||
{
|
||||
_mapPrefix.insert(std::pair<std::string, unsigned int>(defaultPrefix, i + 1));
|
||||
prefixMap.insert(std::pair<std::string, unsigned int>(namePrefix, i + 1));
|
||||
}
|
||||
_nameMap.insert(res);
|
||||
return res + ext;
|
||||
nameMap.insert(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed finding a name
|
||||
// Try with a shorter prefix if possible
|
||||
if (defaultPrefix.length()>1) return getUniqueName(_defaultValue, defaultPrefix.substr(0, defaultPrefix.length()-1), nameIsPath);
|
||||
if (currentPrefixLen>1) return getUniqueName(_defaultValue, isNodeName, defaultPrefix, currentPrefixLen-1);
|
||||
// Try with default prefix if not arleady done
|
||||
if (defaultPrefix != std::string("_")) return getUniqueName(_defaultValue, "_", nameIsPath);
|
||||
if (defaultPrefix != std::string("_")) return getUniqueName(_defaultValue, isNodeName, "_", 1);
|
||||
|
||||
// No more names
|
||||
osg::notify(osg::FATAL) << "No more names available!" << std::endl;
|
||||
OSG_NOTIFY(osg::FATAL) << "No more names available!" << std::endl;
|
||||
_succeeded = false;
|
||||
return "ERROR";
|
||||
}
|
||||
@@ -724,7 +888,7 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo,
|
||||
bool texcoords,
|
||||
Lib3dsMesh * mesh)
|
||||
{
|
||||
osg::notify(osg::DEBUG_INFO) << "Building Mesh" << std::endl;
|
||||
OSG_DEBUG << "Building Mesh" << std::endl;
|
||||
assert(mesh);
|
||||
|
||||
// Write points
|
||||
@@ -734,16 +898,27 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo,
|
||||
for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end();++it)
|
||||
{
|
||||
osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry();
|
||||
assert(g->getVertexArray());
|
||||
if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
|
||||
const osg::Array * basevecs = g->getVertexArray();
|
||||
assert(basevecs);
|
||||
if (!basevecs || basevecs->getNumElements()==0) continue;
|
||||
if (basevecs->getType() == osg::Array::Vec3ArrayType)
|
||||
{
|
||||
// TODO Handle double presision vertices by converting them to float with a warning
|
||||
osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl;
|
||||
const osg::Vec3Array & vecs= *static_cast<const osg::Vec3Array *>(basevecs);
|
||||
copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat);
|
||||
}
|
||||
else if (basevecs->getType() == osg::Array::Vec3dArrayType)
|
||||
{
|
||||
// Handle double presision vertices by converting them to float with a warning
|
||||
OSG_NOTICE << "3DS format only supports single precision vertices. Converting double precision to single." << std::endl;
|
||||
const osg::Vec3dArray & vecs= *static_cast<const osg::Vec3dArray *>(basevecs);
|
||||
copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_NOTIFY(osg::FATAL) << "Vertex array is not Vec3 or Vec3d. Not implemented" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
const osg::Vec3Array & vecs= *static_cast<osg::Vec3Array *>(g->getVertexArray());
|
||||
copyOsgVectorToLib3dsVector(mesh->vertices[it->second], vecs[it->first.first]*mat);
|
||||
}
|
||||
|
||||
// Write texture coords (Texture 0 only)
|
||||
@@ -752,26 +927,25 @@ WriterNodeVisitor::buildMesh(osg::Geode & geo,
|
||||
for(MapIndices::iterator it = index_vert.begin(); it != index_vert.end(); ++it)
|
||||
{
|
||||
osg::Geometry *g = geo.getDrawable( it->first.second )->asGeometry();
|
||||
osg::Array * texarray = g->getTexCoordArray(0);
|
||||
if (texarray)
|
||||
const osg::Array * texarray = g->getNumTexCoordArrays()>=1 ? g->getTexCoordArray(0) : NULL;
|
||||
if (!texarray || texarray->getNumElements()==0) continue;
|
||||
|
||||
if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
|
||||
{
|
||||
if (g->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
const osg::Vec2Array & vecs= *static_cast<osg::Vec2Array *>(texarray);
|
||||
mesh->texcos[it->second][0] = vecs[it->first.first][0];
|
||||
mesh->texcos[it->second][1] = vecs[it->first.first][1];
|
||||
OSG_NOTIFY(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
const osg::Vec2Array & vecs= *static_cast<const osg::Vec2Array *>(texarray);
|
||||
mesh->texcos[it->second][0] = vecs[it->first.first][0];
|
||||
mesh->texcos[it->second][1] = vecs[it->first.first][1];
|
||||
}
|
||||
}
|
||||
lib3ds_file_insert_mesh(file3ds, mesh, _lastMeshIndex);
|
||||
lib3ds_file_insert_mesh(_file3ds, mesh, _lastMeshIndex);
|
||||
++_lastMeshIndex;
|
||||
|
||||
Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance(mesh, mesh->name, NULL, NULL, NULL);
|
||||
lib3ds_file_append_node(file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(_cur3dsNode));
|
||||
lib3ds_file_append_node(_file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(_cur3dsNode));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@@ -781,16 +955,7 @@ WriterNodeVisitor::calcVertices(osg::Geode & geo)
|
||||
for (unsigned int i = 0; i < geo.getNumDrawables(); ++i)
|
||||
{
|
||||
osg::Geometry *g = geo.getDrawable( i )->asGeometry();
|
||||
assert(g->getVertexArray());
|
||||
if (g->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
|
||||
{
|
||||
// TODO Handle double presision vertices by converting them to float with a warning
|
||||
osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl;
|
||||
_succeeded = false;
|
||||
return 0;
|
||||
}
|
||||
const osg::Vec3Array & vecs= *static_cast<osg::Vec3Array *>(g->getVertexArray());
|
||||
numVertice += vecs.getNumElements();
|
||||
if (g->getVertexArray()) numVertice += g->getVertexArray()->getNumElements();
|
||||
}
|
||||
return numVertice;
|
||||
}
|
||||
@@ -806,23 +971,24 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo,
|
||||
unsigned int nbVerticesRemaining = calcVertices(geo); // May set _succeded to false
|
||||
if (!succeeded()) return;
|
||||
|
||||
std::string name( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo") );
|
||||
std::string name( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), true, "geo") );
|
||||
if (!succeeded()) return;
|
||||
Lib3dsMesh *mesh = lib3ds_mesh_new( name.c_str() );
|
||||
if (!mesh)
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Allocation error" << std::endl;
|
||||
OSG_NOTIFY(osg::FATAL) << "Allocation error" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//copyOsgMatrixToLib3dsMatrix(mesh->matrix, mat);
|
||||
lib3ds_mesh_resize_faces (mesh, osg::minimum(nbTrianglesRemaining, MAX_FACES));
|
||||
lib3ds_mesh_resize_vertices(mesh, osg::minimum(nbVerticesRemaining, MAX_VERTICES), texcoords ? 0 : 1, 0); // Not mandatory but will allocate once a big block
|
||||
|
||||
// Test if the mesh will be split and needs sorting
|
||||
if (nbVerticesRemaining >= MAX_VERTICES || nbTrianglesRemaining >= MAX_FACES)
|
||||
{
|
||||
osg::notify(osg::INFO) << "Sorting elements..." << std::endl;
|
||||
OSG_INFO << "Sorting elements..." << std::endl;
|
||||
WriterCompareTriangle cmp(geo, nbVerticesRemaining);
|
||||
std::sort(listTriangles.begin(), listTriangles.end(), cmp);
|
||||
}
|
||||
@@ -851,10 +1017,10 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo,
|
||||
// We can't call a thing like "nbVerticesRemaining -= ...;" because points may be used multiple times.
|
||||
// [Sukender: An optimisation here would take too much time I think.]
|
||||
|
||||
mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), "geo").c_str());
|
||||
mesh = lib3ds_mesh_new( getUniqueName(geo.getName().empty() ? geo.className() : geo.getName(), true, "geo").c_str());
|
||||
if (!mesh)
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Allocation error" << std::endl;
|
||||
OSG_NOTIFY(osg::FATAL) << "Allocation error" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
@@ -877,48 +1043,28 @@ WriterNodeVisitor::buildFaces(osg::Geode & geo,
|
||||
}
|
||||
|
||||
void
|
||||
WriterNodeVisitor::createListTriangle(osg::Geometry * geo,
|
||||
ListTriangle & listTriangles,
|
||||
bool & texcoords,
|
||||
unsigned int & drawable_n)
|
||||
WriterNodeVisitor::createListTriangle(osg::Geometry * geo,
|
||||
ListTriangle & listTriangles,
|
||||
bool & texcoords,
|
||||
unsigned int & drawable_n)
|
||||
{
|
||||
unsigned int nbVertices = 0;
|
||||
const osg::Array * basevecs = geo->getVertexArray();
|
||||
if (!basevecs || basevecs->getNumElements()==0) return;
|
||||
|
||||
// Texture coords
|
||||
const osg::Array * basetexvecs = geo->getNumTexCoordArrays()>=1 ? geo->getTexCoordArray(0) : NULL;
|
||||
if (basetexvecs)
|
||||
{
|
||||
if (geo->getVertexArray() && geo->getVertexArray()->getType() != osg::Array::Vec3ArrayType)
|
||||
unsigned int nb = basetexvecs->getNumElements();
|
||||
if (nb != geo->getVertexArray()->getNumElements())
|
||||
{
|
||||
// TODO Handle double presision vertices by converting them to float with a warning
|
||||
osg::notify(osg::FATAL) << "Vertex array is not Vec3. Not implemented" << std::endl;
|
||||
OSG_NOTIFY(osg::FATAL) << "There are more/less texture coords than vertices (corrupted geometry)" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
const osg::Vec3Array * vecs = geo->getVertexArray() ? static_cast<osg::Vec3Array *>(geo->getVertexArray()) : NULL;
|
||||
if (vecs)
|
||||
{
|
||||
nbVertices = geo->getVertexArray()->getNumElements();
|
||||
// Texture coords
|
||||
if (geo->getTexCoordArray(0) && geo->getTexCoordArray(0)->getType() != osg::Array::Vec2ArrayType)
|
||||
{
|
||||
osg::notify(osg::FATAL) << "Texture coords array is not Vec2. Not implemented" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
const osg::Vec2Array * texvecs = geo->getTexCoordArray(0) ? static_cast<osg::Vec2Array *>(geo->getTexCoordArray(0)) : NULL;
|
||||
if (texvecs)
|
||||
{
|
||||
unsigned int nb = geo->getTexCoordArray(0)->getNumElements();
|
||||
if (nb != geo->getVertexArray()->getNumElements())
|
||||
{
|
||||
osg::notify(osg::FATAL) << "There are more/less texture coords than vertices (corrupted geometry)" << std::endl;
|
||||
_succeeded = false;
|
||||
return;
|
||||
}
|
||||
texcoords = true;
|
||||
}
|
||||
}
|
||||
texcoords = true;
|
||||
}
|
||||
|
||||
if (nbVertices==0) return;
|
||||
|
||||
int material = processStateSet(_currentStateSet.get());
|
||||
|
||||
for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) //Fill the Triangle List
|
||||
@@ -949,8 +1095,12 @@ void WriterNodeVisitor::apply( osg::Geode &node )
|
||||
}
|
||||
if (succeeded() && count > 0)
|
||||
{
|
||||
#if DISABLE_3DS_ANIMATION
|
||||
osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) );
|
||||
buildFaces(node, mat, listTriangles, texcoords); // May set _succeded to false
|
||||
#else
|
||||
buildFaces(node, osg::Matrix(), listTriangles, texcoords); // May set _succeded to false
|
||||
#endif
|
||||
}
|
||||
popStateSet(node.getStateSet());
|
||||
//_nameStack.pop_back();
|
||||
@@ -968,8 +1118,10 @@ void WriterNodeVisitor::apply( osg::Billboard &node )
|
||||
unsigned int count = node.getNumDrawables();
|
||||
ListTriangle listTriangles;
|
||||
bool texcoords = false;
|
||||
osg::notify(osg::NOTICE) << "Warning: 3DS writer is incomplete for Billboards (rotation not implemented)." << std::endl;
|
||||
OSG_NOTICE << "Warning: 3DS writer is incomplete for Billboards (rotation not implemented)." << std::endl;
|
||||
#if DISABLE_3DS_ANIMATION
|
||||
osg::Matrix m( osg::computeLocalToWorld(getNodePath()) );
|
||||
#endif
|
||||
for ( unsigned int i = 0; i < count; i++ )
|
||||
{
|
||||
osg::Geometry *g = node.getDrawable( i )->asGeometry();
|
||||
@@ -983,9 +1135,15 @@ void WriterNodeVisitor::apply( osg::Billboard &node )
|
||||
popStateSet(g->getStateSet()); // May set _succeded to false
|
||||
if (!succeeded()) break;
|
||||
|
||||
osg::Matrix currentBillBoardMat(osg::Matrix::translate(node.getPosition(i)) * m); // TODO handle rotation
|
||||
apply3DSMatrixNode(node, currentBillBoardMat, "bil"); // Add a 3DS matrix node
|
||||
buildFaces(node, currentBillBoardMat, listTriangles, texcoords); // May set _succeded to false
|
||||
osg::Matrix pointLocalMat(osg::Matrix::translate(node.getPosition(i))); // TODO handle rotation
|
||||
#if DISABLE_3DS_ANIMATION
|
||||
osg::Matrix currentBillboardWorldMat(pointLocalMat * m);
|
||||
apply3DSMatrixNode(node, &pointLocalMat, "bil"); // Add a 3DS matrix node (with local matrix)
|
||||
buildFaces(node, currentBillboardWorldMat, listTriangles, texcoords); // May set _succeded to false
|
||||
#else
|
||||
apply3DSMatrixNode(node, &pointLocalMat, "bil"); // Add a 3DS matrix node (with local matrix)
|
||||
buildFaces(node, osg::Matrix(), listTriangles, texcoords); // May set _succeded to false
|
||||
#endif
|
||||
if (!succeeded()) break;
|
||||
}
|
||||
}
|
||||
@@ -1002,7 +1160,12 @@ void WriterNodeVisitor::apply(osg::Group &node)
|
||||
{
|
||||
pushStateSet(node.getStateSet());
|
||||
Lib3dsMeshInstanceNode * parent = _cur3dsNode;
|
||||
apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "grp");
|
||||
#if DISABLE_3DS_ANIMATION
|
||||
osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) );
|
||||
apply3DSMatrixNode(node, &mat, "grp");
|
||||
#else
|
||||
apply3DSMatrixNode(node, NULL, "grp");
|
||||
#endif
|
||||
if (succeeded())
|
||||
traverse(node);
|
||||
_cur3dsNode = parent;
|
||||
@@ -1013,39 +1176,43 @@ void WriterNodeVisitor::apply(osg::MatrixTransform &node)
|
||||
{
|
||||
pushStateSet(node.getStateSet());
|
||||
Lib3dsMeshInstanceNode * parent = _cur3dsNode;
|
||||
apply3DSMatrixNode(node, osg::computeLocalToWorld(getNodePath()), "mtx");
|
||||
#if DISABLE_3DS_ANIMATION
|
||||
osg::Matrix mat( osg::computeLocalToWorld(getNodePath()) );
|
||||
#else
|
||||
osg::Matrix mat( node.getMatrix() );
|
||||
#endif
|
||||
apply3DSMatrixNode(node, &mat, "mtx");
|
||||
if (succeeded())
|
||||
traverse(node);
|
||||
_cur3dsNode = parent;
|
||||
popStateSet(node.getStateSet());
|
||||
}
|
||||
|
||||
void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix & m, const char * const prefix)
|
||||
void WriterNodeVisitor::apply3DSMatrixNode(osg::Node &node, const osg::Matrix * m, const char * const prefix)
|
||||
{
|
||||
// Note: Creating a mesh instance with no transform and then copying the matrix doesn't work (matrix seems to be a temporary/computed value)
|
||||
Lib3dsMeshInstanceNode * parent = _cur3dsNode;
|
||||
Lib3dsMeshInstanceNode * node3ds = NULL;
|
||||
if (m)
|
||||
{
|
||||
osg::Vec3 osgScl, osgPos;
|
||||
osg::Quat osgRot, osgSo;
|
||||
m->decompose(osgPos, osgRot, osgScl, osgSo);
|
||||
|
||||
//const osg::Matrix & m = node.getMatrix();
|
||||
//const osg::Matrix m( osg::computeLocalToWorld(nodePath) ); // [NEEDS TESTING!] 3DS matrices always contain world to local transformation (not local transform; ie. from parent)
|
||||
float pos[3];
|
||||
float scl[3];
|
||||
float rot[4];
|
||||
copyOsgVectorToLib3dsVector(pos, osgPos);
|
||||
copyOsgVectorToLib3dsVector(scl, osgScl);
|
||||
copyOsgQuatToLib3dsQuat(rot, osgRot);
|
||||
node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), true, prefix).c_str(), pos, scl, rot);
|
||||
}
|
||||
else
|
||||
{
|
||||
node3ds = lib3ds_node_new_mesh_instance(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), true, prefix).c_str(), NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// Transform data used to be given to lib3ds_node_new_mesh_instance(), but it seems buggy (pivot problem? bug in conversion?).
|
||||
float pos[3];
|
||||
float scl[3];
|
||||
float rot[4];
|
||||
osg::Vec3 osgScl, osgPos;
|
||||
osg::Quat osgRot, osgSo;
|
||||
m.decompose(osgPos, osgRot, osgScl, osgSo);
|
||||
copyOsgVectorToLib3dsVector(pos, osgPos);
|
||||
copyOsgVectorToLib3dsVector(scl, osgScl);
|
||||
copyOsgQuatToLib3dsQuat(rot, osgRot);
|
||||
Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance
|
||||
(NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), prefix).c_str(), pos, scl, rot);
|
||||
|
||||
//// Create a mesh instance with no transform and then copy the matrix (doesn't work)
|
||||
//Lib3dsMeshInstanceNode * node3ds = lib3ds_node_new_mesh_instance
|
||||
// (NULL, getUniqueName(node.getName().empty() ? node.className() : node.getName(), "mtx").c_str(), NULL, NULL, NULL);
|
||||
// copyOsgMatrixToLib3dsMatrix(node3ds->base.matrix, m);
|
||||
|
||||
lib3ds_file_append_node(file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));
|
||||
lib3ds_file_append_node(_file3ds, reinterpret_cast<Lib3dsNode*>(node3ds), reinterpret_cast<Lib3dsNode*>(parent));
|
||||
_cur3dsNode = node3ds;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,222 +1,227 @@
|
||||
// -*-c++-*-
|
||||
|
||||
/*
|
||||
* 3DS reader/writer for Open Scene Graph
|
||||
*
|
||||
* Copyright (C) ???
|
||||
*
|
||||
* Writing support added 2007 by Sukender (Benoit Neil), http://sukender.free.fr,
|
||||
* strongly inspired by the OBJ writer object by Stephan Huber
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef _3DS_WRITER_NODE_VISITOR_HEADER__
|
||||
#define _3DS_WRITER_NODE_VISITOR_HEADER__
|
||||
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Node>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Material>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/TexGen>
|
||||
#include <osg/TexMat>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "lib3ds/lib3ds.h"
|
||||
#include "WriterCompareTriangle.h"
|
||||
#include <set>
|
||||
|
||||
void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix);
|
||||
|
||||
typedef std::map<std::pair<unsigned int, unsigned int>, unsigned int> MapIndices;
|
||||
typedef std::vector<std::pair<Triangle, int> > ListTriangle; //the int is the drawable of the triangle
|
||||
|
||||
namespace plugin3ds
|
||||
{
|
||||
|
||||
class WriterNodeVisitor: public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
static const unsigned int MAX_VERTICES = 65000;
|
||||
static const unsigned int MAX_FACES = MAX_VERTICES;
|
||||
|
||||
WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & fileName,
|
||||
const osgDB::ReaderWriter::Options* options,
|
||||
const std::string & srcDirectory);
|
||||
|
||||
bool succeeded() const { return _succeeded; }
|
||||
virtual void apply(osg::Geode &node);
|
||||
virtual void apply(osg::Billboard &node);
|
||||
|
||||
virtual void apply(osg::Group &node);
|
||||
virtual void apply(osg::MatrixTransform &node);
|
||||
|
||||
void traverse (osg::Node &node)
|
||||
{
|
||||
pushStateSet(node.getStateSet());
|
||||
osg::NodeVisitor::traverse( node );
|
||||
popStateSet(node.getStateSet());
|
||||
}
|
||||
|
||||
void pushStateSet(osg::StateSet* ss)
|
||||
{
|
||||
if (NULL!=ss) {
|
||||
// Save our current stateset
|
||||
_stateSetStack.push(_currentStateSet.get());
|
||||
|
||||
// merge with node stateset
|
||||
_currentStateSet = static_cast<osg::StateSet*>(_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY));
|
||||
_currentStateSet->merge(*ss);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void popStateSet(osg::StateSet* ss)
|
||||
{
|
||||
if (NULL!=ss) {
|
||||
// restore the previous stateset
|
||||
_currentStateSet = _stateSetStack.top();
|
||||
_stateSetStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void writeMaterials();
|
||||
|
||||
|
||||
|
||||
///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map.
|
||||
class Material {
|
||||
public:
|
||||
Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index=-1);
|
||||
|
||||
int index; ///< Index in the 3DS file
|
||||
osg::Vec4 diffuse, ambient, specular;
|
||||
float shininess;
|
||||
float transparency;
|
||||
bool double_sided;
|
||||
std::string name;
|
||||
osg::ref_ptr<osg::Image> image;
|
||||
bool texture_transparency;
|
||||
bool texture_no_tile;
|
||||
protected:
|
||||
Material() : index(-1) {}
|
||||
|
||||
};
|
||||
|
||||
protected:
|
||||
struct CompareStateSet
|
||||
{
|
||||
bool operator()(const osg::ref_ptr<osg::StateSet>& ss1, const osg::ref_ptr<osg::StateSet>& ss2) const
|
||||
{
|
||||
return *ss1 < *ss2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
WriterNodeVisitor& operator = (const WriterNodeVisitor&) { return *this; }
|
||||
|
||||
/**
|
||||
* Fill the faces field of the mesh and call buildMesh().
|
||||
* \param geo is the geode who contain vertice and faces.
|
||||
* \param mat Local to world matrix applied to the geode
|
||||
* \param listTriangles contain all the meshs faces.
|
||||
* \param texcoords tell us if we have to treat texture coord.
|
||||
*/
|
||||
void buildFaces(osg::Geode & geo, const osg::Matrix & mat, ListTriangle & listTriangles, bool texcoords);
|
||||
|
||||
/**
|
||||
* Calculate the number of vertices in the geode.
|
||||
* \return the number of vertices in the geode.
|
||||
*/
|
||||
unsigned int calcVertices(osg::Geode & geo);
|
||||
|
||||
/**
|
||||
* Build a mesh
|
||||
* \param geo is the geode who contain vertice and faces
|
||||
* \param mat Local to world matrix applied to the geode
|
||||
* \param index_vert is the index used to build the new mesh
|
||||
* \param texcoords tell us if we have to treat texture coord
|
||||
* \param mesh is the mesh with faces filled
|
||||
* \sa See cutScene() about the definition of the boxes for faces sorting.
|
||||
*/
|
||||
void
|
||||
buildMesh(osg::Geode & geo,
|
||||
const osg::Matrix & mat,
|
||||
MapIndices & index_vert,
|
||||
bool texcoords,
|
||||
Lib3dsMesh *mesh);
|
||||
|
||||
/**
|
||||
* Add a vertice to the index and link him with the Triangle index and the drawable.
|
||||
* \param index_vert is the map where the vertice are stored.
|
||||
* \param index is the indice of the vertice's position in the vec3.
|
||||
* \param drawable_n is the number of the drawable.
|
||||
* \return the position of the vertice in the final mesh.
|
||||
*/
|
||||
unsigned int
|
||||
getMeshIndexForGeometryIndex(MapIndices & index_vert,
|
||||
unsigned int index,
|
||||
unsigned int drawable_n);
|
||||
/**
|
||||
* Create the list of faces from the geode.
|
||||
* \param geo is the geode to study.
|
||||
* \param listTriangles is the list to fill.
|
||||
* \param texcoords tell us if we have to treat texture coord.
|
||||
* \param drawable_n tell us which drawable we are building.
|
||||
*/
|
||||
void createListTriangle(osg::Geometry * geo,
|
||||
ListTriangle & listTriangles,
|
||||
bool & texcoords,
|
||||
unsigned int & drawable_n);
|
||||
|
||||
int processStateSet(osg::StateSet* stateset);
|
||||
|
||||
std::string getUniqueName(const std::string& defaultvalue="", const std::string & defaultPrefix = "", bool nameIsPath = false);
|
||||
std::string export3DSTexture(const osg::Image * image, const std::string & fileName);
|
||||
|
||||
typedef std::stack<osg::ref_ptr<osg::StateSet> > StateSetStack;
|
||||
typedef std::map< osg::ref_ptr<osg::StateSet>, Material, CompareStateSet> MaterialMap;
|
||||
|
||||
void apply3DSMatrixNode(osg::Node &node, const osg::Matrix & m, const char * const prefix);
|
||||
|
||||
bool _succeeded;
|
||||
std::string _directory;
|
||||
std::string _srcDirectory;
|
||||
Lib3dsFile * file3ds;
|
||||
StateSetStack _stateSetStack;
|
||||
osg::ref_ptr<osg::StateSet> _currentStateSet;
|
||||
std::map<std::string, unsigned int> _mapPrefix; ///< List of next number to use in unique name generation, for each prefix
|
||||
std::set<std::string> _nameMap;
|
||||
MaterialMap _materialMap;
|
||||
unsigned int _lastMaterialIndex;
|
||||
unsigned int _lastMeshIndex;
|
||||
Lib3dsMeshInstanceNode * _cur3dsNode;
|
||||
const osgDB::ReaderWriter::Options* options;
|
||||
unsigned int _imageCount;
|
||||
bool _extendedFilePaths;
|
||||
std::set<osg::Image *> _imageSet;
|
||||
};
|
||||
|
||||
// end namespace plugin3ds
|
||||
}
|
||||
|
||||
#endif
|
||||
// -*-c++-*-
|
||||
|
||||
/*
|
||||
* 3DS reader/writer for Open Scene Graph
|
||||
*
|
||||
* Copyright (C) ???
|
||||
*
|
||||
* Writing support added 2009 by Sukender (Benoit Neil), http://sukender.free.fr,
|
||||
* strongly inspired by the OBJ writer object by Stephan Huber
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef _3DS_WRITER_NODE_VISITOR_HEADER__
|
||||
#define _3DS_WRITER_NODE_VISITOR_HEADER__
|
||||
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osg/Node>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Material>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/TexGen>
|
||||
#include <osg/TexMat>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "lib3ds/lib3ds.h"
|
||||
#include "WriterCompareTriangle.h"
|
||||
#include <set>
|
||||
|
||||
void copyOsgMatrixToLib3dsMatrix(Lib3dsMatrix lib3ds_matrix, const osg::Matrix& osg_matrix);
|
||||
|
||||
typedef std::map<std::pair<unsigned int, unsigned int>, unsigned int> MapIndices;
|
||||
typedef std::vector<std::pair<Triangle, int> > ListTriangle; //the int is the drawable of the triangle
|
||||
|
||||
namespace plugin3ds
|
||||
{
|
||||
|
||||
class WriterNodeVisitor: public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
static const unsigned int MAX_VERTICES = 65000;
|
||||
static const unsigned int MAX_FACES = MAX_VERTICES;
|
||||
|
||||
WriterNodeVisitor(Lib3dsFile * file3ds, const std::string & fileName,
|
||||
const osgDB::ReaderWriter::Options* options,
|
||||
const std::string & srcDirectory);
|
||||
|
||||
bool succeeded() const { return _succeeded; }
|
||||
virtual void apply(osg::Geode &node);
|
||||
virtual void apply(osg::Billboard &node);
|
||||
|
||||
virtual void apply(osg::Group &node);
|
||||
virtual void apply(osg::MatrixTransform &node);
|
||||
|
||||
void traverse (osg::Node &node)
|
||||
{
|
||||
pushStateSet(node.getStateSet());
|
||||
osg::NodeVisitor::traverse( node );
|
||||
popStateSet(node.getStateSet());
|
||||
}
|
||||
|
||||
void pushStateSet(osg::StateSet* ss)
|
||||
{
|
||||
if (NULL!=ss) {
|
||||
// Save our current stateset
|
||||
_stateSetStack.push(_currentStateSet.get());
|
||||
|
||||
// merge with node stateset
|
||||
_currentStateSet = static_cast<osg::StateSet*>(_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY));
|
||||
_currentStateSet->merge(*ss);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void popStateSet(osg::StateSet* ss)
|
||||
{
|
||||
if (NULL!=ss) {
|
||||
// restore the previous stateset
|
||||
_currentStateSet = _stateSetStack.top();
|
||||
_stateSetStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void writeMaterials();
|
||||
|
||||
|
||||
|
||||
///\todo Add support for 2nd texture, opacity_map, bump_map, specular_map, shininess_map, self_illum_map, reflection_map.
|
||||
class Material {
|
||||
public:
|
||||
Material(WriterNodeVisitor & writerNodeVisitor, osg::StateSet * stateset, osg::Material* mat, osg::Texture* tex, int index=-1);
|
||||
|
||||
int index; ///< Index in the 3DS file
|
||||
osg::Vec4 diffuse, ambient, specular;
|
||||
float shininess;
|
||||
float transparency;
|
||||
bool double_sided;
|
||||
std::string name;
|
||||
osg::ref_ptr<osg::Image> image;
|
||||
bool texture_transparency;
|
||||
bool texture_no_tile;
|
||||
protected:
|
||||
Material() : index(-1) {}
|
||||
|
||||
};
|
||||
|
||||
protected:
|
||||
struct CompareStateSet
|
||||
{
|
||||
bool operator()(const osg::ref_ptr<osg::StateSet>& ss1, const osg::ref_ptr<osg::StateSet>& ss2) const
|
||||
{
|
||||
return *ss1 < *ss2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
WriterNodeVisitor& operator = (const WriterNodeVisitor&) { return *this; }
|
||||
|
||||
/**
|
||||
* Fill the faces field of the mesh and call buildMesh().
|
||||
* \param geo is the geode who contain vertice and faces.
|
||||
* \param mat Local to world matrix applied to the geode
|
||||
* \param listTriangles contain all the meshs faces.
|
||||
* \param texcoords tell us if we have to treat texture coord.
|
||||
*/
|
||||
void buildFaces(osg::Geode & geo, const osg::Matrix & mat, ListTriangle & listTriangles, bool texcoords);
|
||||
|
||||
/**
|
||||
* Calculate the number of vertices in the geode.
|
||||
* \return the number of vertices in the geode.
|
||||
*/
|
||||
unsigned int calcVertices(osg::Geode & geo);
|
||||
|
||||
/**
|
||||
* Build a mesh
|
||||
* \param geo is the geode who contain vertice and faces
|
||||
* \param mat Local to world matrix applied to the geode
|
||||
* \param index_vert is the index used to build the new mesh
|
||||
* \param texcoords tell us if we have to treat texture coord
|
||||
* \param mesh is the mesh with faces filled
|
||||
* \sa See cutScene() about the definition of the boxes for faces sorting.
|
||||
*/
|
||||
void
|
||||
buildMesh(osg::Geode & geo,
|
||||
const osg::Matrix & mat,
|
||||
MapIndices & index_vert,
|
||||
bool texcoords,
|
||||
Lib3dsMesh *mesh);
|
||||
|
||||
/**
|
||||
* Add a vertice to the index and link him with the Triangle index and the drawable.
|
||||
* \param index_vert is the map where the vertice are stored.
|
||||
* \param index is the indice of the vertice's position in the vec3.
|
||||
* \param drawable_n is the number of the drawable.
|
||||
* \return the position of the vertice in the final mesh.
|
||||
*/
|
||||
unsigned int
|
||||
getMeshIndexForGeometryIndex(MapIndices & index_vert,
|
||||
unsigned int index,
|
||||
unsigned int drawable_n);
|
||||
/**
|
||||
* Create the list of faces from the geode.
|
||||
* \param geo is the geode to study.
|
||||
* \param listTriangles is the list to fill.
|
||||
* \param texcoords tell us if we have to treat texture coord.
|
||||
* \param drawable_n tell us which drawable we are building.
|
||||
*/
|
||||
void createListTriangle(osg::Geometry * geo,
|
||||
ListTriangle & listTriangles,
|
||||
bool & texcoords,
|
||||
unsigned int & drawable_n);
|
||||
|
||||
int processStateSet(osg::StateSet* stateset);
|
||||
|
||||
std::string getUniqueName(const std::string& defaultvalue, bool isNodeName, const std::string & defaultPrefix = "", int currentPrefixLen = -1);
|
||||
std::string export3DSTexture(const osg::Image * image, const std::string & fileName);
|
||||
|
||||
typedef std::stack<osg::ref_ptr<osg::StateSet> > StateSetStack;
|
||||
typedef std::map< osg::ref_ptr<osg::StateSet>, Material, CompareStateSet> MaterialMap;
|
||||
|
||||
void apply3DSMatrixNode(osg::Node &node, const osg::Matrix * m, const char * const prefix);
|
||||
|
||||
bool _succeeded;
|
||||
std::string _directory;
|
||||
std::string _srcDirectory;
|
||||
Lib3dsFile * _file3ds;
|
||||
StateSetStack _stateSetStack;
|
||||
osg::ref_ptr<osg::StateSet> _currentStateSet;
|
||||
typedef std::map<std::string, unsigned int> PrefixMap;
|
||||
PrefixMap _nodePrefixMap; ///< List of next number to use in unique name generation, for each prefix
|
||||
PrefixMap _imagePrefixMap;
|
||||
typedef std::set<std::string> NameMap;
|
||||
NameMap _nodeNameMap;
|
||||
NameMap _imageNameMap;
|
||||
MaterialMap _materialMap;
|
||||
unsigned int _lastMaterialIndex;
|
||||
unsigned int _lastMeshIndex;
|
||||
Lib3dsMeshInstanceNode * _cur3dsNode;
|
||||
const osgDB::ReaderWriter::Options* _options;
|
||||
unsigned int _imageCount;
|
||||
bool _extendedFilePaths;
|
||||
typedef std::map<osg::Image*, std::string> ImageSet;
|
||||
ImageSet _imageSet; ///< Map used to avoid renaming and writing twice an image
|
||||
};
|
||||
|
||||
// end namespace plugin3ds
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -788,8 +788,6 @@ extern LIB3DSAPI void lib3ds_matrix_rotate(float m[4][4], float angle, float ax,
|
||||
extern LIB3DSAPI void lib3ds_matrix_camera(float m[4][4], float pos[3], float tgt[3], float roll);
|
||||
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
extern LIB3DSAPI void setByteOrder();
|
||||
|
||||
/* Definitions for compatibility with previous lib3DS used: */
|
||||
typedef float Lib3dsMatrix[4][4];
|
||||
typedef float Lib3dsVector[3];
|
||||
|
||||
@@ -446,13 +446,14 @@ mdata_read(Lib3dsFile *file, Lib3dsIo *io) {
|
||||
|
||||
static int
|
||||
compare_node_id( const void *a, const void *b ) {
|
||||
return (*((Lib3dsNode**)a))->node_id - (*((Lib3dsNode**)b))->node_id;
|
||||
return (int)((*((Lib3dsNode**)a))->node_id) - (int)((*((Lib3dsNode**)b))->node_id);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_node_id2( const void *a, const void *b ) {
|
||||
return *((unsigned short*)a) - (*((Lib3dsNode**)b))->node_id;
|
||||
// not a is a pointer in the calling bsearch routine the user_id is an unsigned, while the node_id is an unsigned short?!
|
||||
return (int)(*((unsigned*)a)) - (int)((*((Lib3dsNode**)b))->node_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,11 @@
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#if defined __sun || defined __hpux
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#else
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
|
||||
@@ -17,16 +17,6 @@
|
||||
*/
|
||||
#include "lib3ds_impl.h"
|
||||
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
#include "lib3ds.h" // For setByteOrder()
|
||||
#include <osg/Endian>
|
||||
|
||||
static bool s_requiresByteSwap = false;
|
||||
extern LIB3DSAPI void setByteOrder()
|
||||
{
|
||||
s_requiresByteSwap = osg::getCpuByteOrder()==osg::BigEndian;
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
|
||||
typedef union {
|
||||
uint32_t dword_value;
|
||||
@@ -171,12 +161,6 @@ lib3ds_io_read_word(Lib3dsIo *io) {
|
||||
lib3ds_io_read(io, b, 2);
|
||||
w = ((uint16_t)b[1] << 8) |
|
||||
((uint16_t)b[0]);
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes2((char*)&w);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
return(w);
|
||||
}
|
||||
|
||||
@@ -195,12 +179,6 @@ lib3ds_io_read_dword(Lib3dsIo *io) {
|
||||
((uint32_t)b[2] << 16) |
|
||||
((uint32_t)b[1] << 8) |
|
||||
((uint32_t)b[0]);
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&d);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
return(d);
|
||||
}
|
||||
|
||||
@@ -230,12 +208,6 @@ lib3ds_io_read_intw(Lib3dsIo *io) {
|
||||
lib3ds_io_read(io, b, 2);
|
||||
w = ((uint16_t)b[1] << 8) |
|
||||
((uint16_t)b[0]);
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes2((char*)&w);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
return((int16_t)w);
|
||||
}
|
||||
|
||||
@@ -254,12 +226,6 @@ lib3ds_io_read_intd(Lib3dsIo *io) {
|
||||
((uint32_t)b[2] << 16) |
|
||||
((uint32_t)b[1] << 8) |
|
||||
((uint32_t)b[0]);
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&d);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
return((int32_t)d);
|
||||
}
|
||||
|
||||
@@ -278,12 +244,6 @@ lib3ds_io_read_float(Lib3dsIo *io) {
|
||||
((uint32_t)b[2] << 16) |
|
||||
((uint32_t)b[1] << 8) |
|
||||
((uint32_t)b[0]);
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&(d.dword_value));
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
return d.float_value;
|
||||
}
|
||||
|
||||
@@ -360,18 +320,11 @@ lib3ds_io_write_byte(Lib3dsIo *io, uint8_t b) {
|
||||
*/
|
||||
void
|
||||
lib3ds_io_write_word(Lib3dsIo *io, uint16_t w) {
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes2((char*)&w);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
|
||||
uint8_t b[2];
|
||||
|
||||
assert(io);
|
||||
b[1] = ((uint16_t)w & 0xFF00) >> 8;
|
||||
b[0] = ((uint16_t)w & 0x00FF);
|
||||
b[1] = uint8_t((w & 0xFF00) >> 8);
|
||||
b[0] = uint8_t(w & 0x00FF);
|
||||
if (lib3ds_io_write(io, b, 2) != 2) {
|
||||
lib3ds_io_write_error(io);
|
||||
}
|
||||
@@ -383,12 +336,6 @@ lib3ds_io_write_word(Lib3dsIo *io, uint16_t w) {
|
||||
*/
|
||||
void
|
||||
lib3ds_io_write_dword(Lib3dsIo *io, uint32_t d) {
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&d);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
uint8_t b[4];
|
||||
|
||||
assert(io);
|
||||
@@ -419,17 +366,11 @@ lib3ds_io_write_intb(Lib3dsIo *io, int8_t b) {
|
||||
*/
|
||||
void
|
||||
lib3ds_io_write_intw(Lib3dsIo *io, int16_t w) {
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes2((char*)&w);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
uint8_t b[2];
|
||||
|
||||
assert(io);
|
||||
b[1] = ((uint16_t)w & 0xFF00) >> 8;
|
||||
b[0] = ((uint16_t)w & 0x00FF);
|
||||
b[1] = uint8_t((w & 0xFF00) >> 8);
|
||||
b[0] = uint8_t(w & 0x00FF);
|
||||
if (lib3ds_io_write(io, b, 2) != 2) {
|
||||
lib3ds_io_write_error(io);
|
||||
}
|
||||
@@ -441,12 +382,6 @@ lib3ds_io_write_intw(Lib3dsIo *io, int16_t w) {
|
||||
*/
|
||||
void
|
||||
lib3ds_io_write_intd(Lib3dsIo *io, int32_t d) {
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&d);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
uint8_t b[4];
|
||||
|
||||
assert(io);
|
||||
@@ -465,17 +400,11 @@ lib3ds_io_write_intd(Lib3dsIo *io, int32_t d) {
|
||||
*/
|
||||
void
|
||||
lib3ds_io_write_float(Lib3dsIo *io, float l) {
|
||||
uint8_t b[4];
|
||||
uint8_t b[4];
|
||||
Lib3dsDwordFloat d;
|
||||
|
||||
assert(io);
|
||||
d.float_value = l;
|
||||
/* --- Code for OpenSceneGraph --- */
|
||||
if (s_requiresByteSwap)
|
||||
{
|
||||
osg::swapBytes4((char*)&d.dword_value);
|
||||
}
|
||||
/* --- (end) Code for OpenSceneGraph --- */
|
||||
b[3] = (uint8_t)(((uint32_t)d.dword_value & 0xFF000000) >> 24);
|
||||
b[2] = (uint8_t)(((uint32_t)d.dword_value & 0x00FF0000) >> 16);
|
||||
b[1] = (uint8_t)(((uint32_t)d.dword_value & 0x0000FF00) >> 8);
|
||||
|
||||
@@ -166,7 +166,7 @@ lib3ds_node_new_mesh_instance(Lib3dsMesh *mesh, const char *instance_name, float
|
||||
|
||||
Lib3dsCameraNode*
|
||||
lib3ds_node_new_camera(Lib3dsCamera *camera) {
|
||||
Lib3dsNode *node = lib3ds_node_new(LIB3DS_NODE_CAMERA);
|
||||
Lib3dsNode *node;
|
||||
Lib3dsCameraNode *n;
|
||||
|
||||
assert(camera);
|
||||
|
||||
@@ -235,7 +235,7 @@ setup_segment(Lib3dsTrack *track, int index, Lib3dsKey *pp, Lib3dsKey *p0, Lib3d
|
||||
*pp = track->keys[track->nkeys - 2];
|
||||
pp->frame = track->keys[track->nkeys - 2].frame - (track->keys[track->nkeys - 1].frame - track->keys[0].frame);
|
||||
}
|
||||
else ip = -1; // Avoids a compiler warning
|
||||
else ip = -1; // Avoids a compiler warning
|
||||
}
|
||||
|
||||
*p0 = track->keys[index - 1];
|
||||
@@ -250,6 +250,7 @@ setup_segment(Lib3dsTrack *track, int index, Lib3dsKey *pp, Lib3dsKey *p0, Lib3d
|
||||
*pn = track->keys[1];
|
||||
pn->frame = track->keys[1].frame + (track->keys[track->nkeys-1].frame - track->keys[0].frame);
|
||||
}
|
||||
else in = -1; // Avoids a compiler warning
|
||||
}
|
||||
|
||||
if (track->type == LIB3DS_TRACK_QUAT) {
|
||||
|
||||
Reference in New Issue
Block a user