The current fbx writer doesn't seem to support BIND_PER_VERTEX for
normals. If you have this type of geometry it, exports the the first
normal in the array over the whole primitive set. I don't know that the
attached change should be applied as is, since I don't know enough about
DrawArrays and Draw Elements to know if the indices for the vertices and
normals are guaranteed to be the same. I tried it on a couple models
and that seemed to be the case.
The alternative is to have the logic down in
setControlPointAndNormalsAndUV and set the normals there with the
"vertexIndex" if the binding type is per vertex. Not sure what is cleaner.
Martins
// -*-c++-*-
/*
* FBX writer for Open Scene Graph
*
* Copyright (C) 2009
*
* Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil -
http://sukender.free.fr)
*
* 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 _FBX_WRITER_NODE_VISITOR_HEADER__
#define _FBX_WRITER_NODE_VISITOR_HEADER__
#include <map>
#include <set>
#include <stack>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/NodeVisitor>
#include <osg/PrimitiveSet>
#include <osgDB/FileNameUtils>
#include <osgDB/ReaderWriter>
#if defined(_MSC_VER)
#pragma warning( disable : 4505 )
#endif
#include <fbxsdk.h>
struct Triangle
{
unsigned int t1;
unsigned int t2;
unsigned int t3;
unsigned int normalIndex1; ///< Normal index for all bindings except
BIND_PER_VERTEX and BIND_OFF.
unsigned int normalIndex2;
unsigned int normalIndex3;
int material;
};
struct VertexIndex
{
VertexIndex(unsigned int vertexIndex, unsigned int drawableIndex, unsigned
int normalIndex)
: vertexIndex(vertexIndex), drawableIndex(drawableIndex),
normalIndex(normalIndex)
{}
VertexIndex(const VertexIndex & v) : vertexIndex(v.vertexIndex),
drawableIndex(v.drawableIndex), normalIndex(v.normalIndex) {}
unsigned int vertexIndex; ///< Index of the vertice position in the
vec3 array
unsigned int drawableIndex;
unsigned int normalIndex; ///< Normal index for all bindings except
BIND_PER_VERTEX and BIND_OFF.
bool operator<(const VertexIndex & v) const {
if (drawableIndex!=v.drawableIndex) return
drawableIndex<v.drawableIndex;
return vertexIndex<v.vertexIndex;
}
};
typedef std::vector<std::pair<Triangle, int> > ListTriangle; //the int is the
drawable of the triangle
typedef std::map<VertexIndex, unsigned int> MapIndices; ///< Map OSG
indices to FBX mesh indices
namespace pluginfbx
{
///\author Capo (Thibault Caporal), Sukender (Benoit Neil)
class WriterNodeVisitor: public osg::NodeVisitor
{
public:
WriterNodeVisitor(KFbxScene* pScene,
KFbxSdkManager* pSdkManager,
const std::string& fileName,
const osgDB::ReaderWriter::Options* options,
const std::string& srcDirectory) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pScene(pScene),
_pSdkManager(pSdkManager),
_currentStateSet(new osg::StateSet()),
_lastMaterialIndex(0),
_lastMeshIndex(0),
_lastGeneratedImageFileName(0),
_curFbxNode(pScene->GetRootNode()),
_options(options),
_succeedLastApply(true),
_directory(osgDB::getFilePath(fileName)),
_srcDirectory(srcDirectory)
{}
///Tell us if last Node succeed traversing.
bool succeedLastApply() const { return _succeedLastApply; }
///Set the flag _succeedLastApply to false.
void failedApply() { _succeedLastApply = false; }
virtual void apply(osg::Geode& 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(const osg::StateSet* ss)
{
if (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(const osg::StateSet* ss)
{
if (ss)
{
// restore the previous stateset
_currentStateSet = _stateSetStack.top();
_stateSetStack.pop();
}
}
/// Copy the texture file in current path.
void copyTexture();
typedef std::map<const osg::Image*, std::string> ImageSet;
typedef std::set<std::string> ImageFilenameSet; // Sub-optimal
because strings are doubled (in ImageSet). Moreover, an unordered_set (=
hashset) would be more efficient (Waiting for unordered_set to be included in
C++ standard ;) ).
///\todo Add support for 2nd texture, opacity_map, bump_map,
specular_map, shininess_map, self_illum_map, reflection_map.
class Material
{
public:
///Create a KfbxMaterial and KfbxTexture from osg::Texture and
osg::Material.
Material(WriterNodeVisitor& writerNodeVisitor,
const std::string& srcDirectory,
const osg::StateSet* stateset,
const osg::Material* mat,
const osg::Texture* tex,
KFbxSdkManager* pSdkManager,
const std::string& directory,
ImageSet& imageSet,
ImageFilenameSet& imageFilenameSet,
unsigned int& lastGeneratedImageFileName,
int index = -1);
KFbxTexture* getFbxTexture() const
{
return _fbxTexture;
}
KFbxSurfaceMaterial* getFbxMaterial() const
{
return _fbxMaterial;
}
const osg::Image* getOsgImage() const
{
return _osgImage;
}
const int getIndex() const
{
return _index;
}
void setIndex(int index)
{
_index = index;
}
private:
KFbxSurfacePhong* _fbxMaterial;
KFbxTexture* _fbxTexture;
int _index;///< Index in the Map
const osg::Image* _osgImage;
const std::string& _directory;
};
protected:
/// Compares StateSets.
///\todo It may be useful to compare stack of pointers (see
pushStateset()) in order to keep the same number of FBX materials when doing
reading and then writing without further processing.
struct CompareStateSet
{
bool operator () (const osg::ref_ptr<const osg::StateSet>& ss1,
const osg::ref_ptr<const osg::StateSet>& ss2) const
{
return *ss1 < *ss2;
}
};
private:
/**
* Fill the faces field of the mesh and call buildMesh().
* \param geo is the geode which contains the vertices and faces.
* \param listTriangles contain all the mesh's faces.
* \param texcoords tell us if we have to handle texture coordinates.
*/
void buildFaces(const osg::Geode& geo,
ListTriangle& listTriangles,
bool texcoords);
/// Set the layer for texture and Material in layer 0.
void setLayerTextureAndMaterial(KFbxMesh* mesh);
/// Set Vertices, normals, and UVs
void setControlPointAndNormalsAndUV(const osg::Geode& geo,
MapIndices& index_vert,
bool texcoords,
KFbxMesh* fbxMesh);
/**
* 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(const osg::Geometry* geo,
ListTriangle& listTriangles,
bool& texcoords,
unsigned int& drawable_n);
///Store the material of the stateset in the MaterialMap.
int processStateSet(const osg::StateSet* stateset);
typedef std::stack<osg::ref_ptr<osg::StateSet> > StateSetStack;
typedef std::map<osg::ref_ptr<const osg::StateSet>, Material,
CompareStateSet> MaterialMap;
///We need this for every new Node we create.
KFbxSdkManager* _pSdkManager;
///Tell us if the last apply succeed, useful to stop going through the
graph.
bool _succeedLastApply;
///The current directory.
std::string _directory;
///The Scene to save.
KFbxScene* _pScene;
///The current Fbx Node.
KFbxNode* _curFbxNode;
///The Stack of different stateSet.
StateSetStack _stateSetStack;
///The current stateSet.
osg::ref_ptr<osg::StateSet> _currentStateSet;
///We store the fbx Materials and Textures in this map.
MaterialMap _materialMap;
ImageSet _imageSet;
ImageFilenameSet _imageFilenameSet;
unsigned int _lastGeneratedImageFileName;
unsigned int _lastMaterialIndex;
unsigned int _lastMeshIndex;
const osgDB::ReaderWriter::Options* _options;
const std::string _srcDirectory;
};
// end namespace pluginfbx
}
#endif // _FBX_WRITER_NODE_VISITOR_HEADER__
// -*-c++-*-
/*
* FBX writer for Open Scene Graph
*
* Copyright (C) 2009
*
* Writing support added 2009 by Thibault Caporal and Sukender (Benoit Neil -
http://sukender.free.fr)
*
* 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/
*/
#include <cassert>
#include <sstream>
#include <iostream>
#include <map>
#include <osg/CullFace>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/NodeVisitor>
#include <osg/PrimitiveSet>
#include <osgDB/FileUtils>
#include <osgDB/WriteFile>
#include "WriterNodeVisitor.h"
#include <limits.h>
// Use namespace qualification to avoid static-link symbol collitions
// from multiply defined symbols.
namespace pluginfbx
{
/** writes all primitives of a primitive-set out to a stream, decomposes quads
to triangles, line-strips to lines etc */
class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor
{
public:
PrimitiveIndexWriter(const osg::Geometry* geo,
ListTriangle& listTriangles,
unsigned int drawable_n,
unsigned int material) :
_hasNormalCoords(geo->getNormalArray() != NULL),
_hasTexCoords(geo->getTexCoordArray(0) != NULL),
_geo(geo),
_lastFaceIndex(0),
_listTriangles(listTriangles),
_drawable_n(drawable_n),
_material(material),
//_iPrimitiveSet(iPrimitiveSet),
_curNormalIndex(0),
_normalBinding(geo->getNormalBinding())
{
if (!geo->getNormalArray() ||
geo->getNormalArray()->getNumElements()==0)
{
_normalBinding = osg::Geometry::BIND_OFF; // Turn off
binding if there is no normal data
}
reset();
}
void reset() { _curNormalIndex = 0; }
unsigned int getNextFaceIndex() { return _lastFaceIndex; }
virtual void setVertexArray(unsigned int, const osg::Vec2*) {}
virtual void setVertexArray(unsigned int, const osg::Vec3*) {}
virtual void setVertexArray(unsigned int, const osg::Vec4*) {}
virtual void setVertexArray(unsigned int, const osg::Vec2d*) {}
virtual void setVertexArray(unsigned int, const osg::Vec3d*) {}
virtual void setVertexArray(unsigned int, const osg::Vec4d*) {}
// operator for triangles
void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
{
Triangle triangle;
triangle.t1 = i1;
triangle.t2 = i2;
triangle.t3 = i3;
if (_normalBinding == osg::Geometry::BIND_PER_VERTEX){
triangle.normalIndex1 = i1;
triangle.normalIndex2 = i2;
triangle.normalIndex3 = i3;
}
else{
triangle.normalIndex1 = _curNormalIndex;
triangle.normalIndex2 = _curNormalIndex;
triangle.normalIndex3 = _curNormalIndex;
}
triangle.material = _material;
_listTriangles.push_back(std::make_pair(triangle, _drawable_n));
}
virtual void begin(GLenum mode)
{
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert)
{
_indexCache.push_back(vert);
}
virtual void end()
{
if (!_indexCache.empty())
{
drawElements(_modeCache, _indexCache.size(), &_indexCache.front());
}
}
virtual void drawArrays(GLenum mode, GLint first, GLsizei count);
virtual void drawElements(GLenum mode, GLsizei count, const GLubyte*
indices)
{
drawElementsImplementation<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLushort*
indices)
{
drawElementsImplementation<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode, GLsizei count, const GLuint* indices)
{
drawElementsImplementation<GLuint>(mode, count, indices);
}
protected:
template <typename T> void drawElementsImplementation(GLenum mode, GLsizei
count, const T* indices)
{
if (indices==0 || count==0) return;
typedef const T* IndexPointer;
switch (mode)
{
case GL_TRIANGLES:
{
IndexPointer ilast = indices + count;
for (IndexPointer iptr = indices; iptr < ilast; iptr+=3)
{
writeTriangle(iptr[0], iptr[1], iptr[2]);
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
}
break;
}
case GL_TRIANGLE_STRIP:
{
IndexPointer iptr = indices;
for (GLsizei i = 2; i < count; ++i, ++iptr)
{
if (i & 1) writeTriangle(iptr[0], iptr[2], iptr[1]);
else writeTriangle(iptr[0], iptr[1], iptr[2]);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
}
case GL_QUADS:
{
IndexPointer iptr = indices;
for (GLsizei i = 3; i < count; i += 4, iptr += 4)
{
writeTriangle(iptr[0], iptr[1], iptr[2]);
writeTriangle(iptr[0], iptr[2], iptr[3]);
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
}
break;
}
case GL_QUAD_STRIP:
{
IndexPointer iptr = indices;
for (GLsizei i = 3; i < count; i += 2, iptr += 2)
{
writeTriangle(iptr[0], iptr[1], iptr[2]);
writeTriangle(iptr[1], iptr[3], iptr[2]);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
}
case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN
case GL_TRIANGLE_FAN:
{
IndexPointer iptr = indices;
unsigned int first = *iptr;
++iptr;
for (GLsizei i = 2; i < count; ++i, ++iptr)
{
writeTriangle(first, iptr[0], iptr[1]);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
}
case GL_POINTS:
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
// Not handled
break;
default:
// uhm should never come to this point :)
break;
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE_SET)
++_curNormalIndex;
}
private:
PrimitiveIndexWriter& operator = (const PrimitiveIndexWriter&); // { return
*this; }
unsigned int _drawable_n;
ListTriangle& _listTriangles;
GLenum _modeCache;
std::vector<GLuint> _indexCache;
bool _hasNormalCoords, _hasTexCoords;
const osg::Geometry* _geo;
unsigned int _lastFaceIndex;
int _material;
unsigned int _curNormalIndex;
osg::Geometry::AttributeBinding _normalBinding;
KFbxMesh* _mesh;
};
void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count)
{
unsigned int pos=first;
switch (mode)
{
case GL_TRIANGLES:
for (GLsizei i = 2; i < count; i += 3, pos += 3)
{
writeTriangle(pos, pos + 1, pos + 2);
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
}
break;
case GL_TRIANGLE_STRIP:
for (GLsizei i = 2; i < count; ++i, ++pos)
{
if (i & 1) writeTriangle(pos, pos + 2, pos + 1);
else writeTriangle(pos, pos + 1, pos + 2);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
case GL_QUADS:
for (GLsizei i = 3; i < count; i += 4, pos += 4)
{
writeTriangle(pos, pos + 1, pos + 2);
writeTriangle(pos, pos + 2, pos + 3);
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
}
break;
case GL_QUAD_STRIP:
for (GLsizei i = 3; i < count; i += 2, pos += 2)
{
writeTriangle(pos, pos + 1, pos + 2);
writeTriangle(pos + 1, pos + 3, pos + 2);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
case GL_POLYGON: // treat polygons as GL_TRIANGLE_FAN
case GL_TRIANGLE_FAN:
pos = first + 1;
for (GLsizei i = 2; i < count; ++i, ++pos)
{
writeTriangle(first, pos, pos+1);
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE)
++_curNormalIndex;
break;
case GL_POINTS:
case GL_LINES:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
default:
osg::notify(osg::WARN) << "WriterNodeVisitor :: can't handle mode " <<
mode << std::endl;
break;
}
if (_normalBinding == osg::Geometry::BIND_PER_PRIMITIVE_SET)
++_curNormalIndex;
}
// If 'to' is in a subdirectory of 'from' then this function returns the
// subpath. Otherwise it just returns the file name.
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());
}
//std::string testA = getPathRelative("C:\\a\\b", "C:\\a/b/d/f");
//std::string testB = getPathRelative("C:\\a\\d", "C:\\a/b/d/f");
//std::string testC = getPathRelative("C:\\ab", "C:\\a/b/d/f");
//std::string testD = getPathRelative("a/d", "a/d");
WriterNodeVisitor::Material::Material(WriterNodeVisitor& writerNodeVisitor,
const std::string& srcDirectory,
const osg::StateSet* stateset,
const osg::Material* mat,
const osg::Texture* tex,
KFbxSdkManager* pSdkManager,
const std::string& directory,
ImageSet& imageSet,
ImageFilenameSet& imageFilenameSet,
unsigned int& lastGeneratedImageFileName,
int index) :
_index(index),
_fbxMaterial(NULL),
_fbxTexture(NULL),
_osgImage(NULL),
_directory(directory)
{
osg::Vec4 diffuse(1,1,1,1),
ambient(0.2,0.2,0.2,1),
specular(0,0,0,1),
emission(1,1,1,1);
float shininess(0);
float transparency(0);
if (mat)
{
assert(stateset);
diffuse = mat->getDiffuse(osg::Material::FRONT);
ambient = mat->getAmbient(osg::Material::FRONT);
specular = mat->getSpecular(osg::Material::FRONT);
shininess = mat->getShininess(osg::Material::FRONT);
emission = mat->getEmission(osg::Material::FRONT);
transparency = 1 - diffuse.w();
const osg::StateAttribute* attribute =
stateset->getAttribute(osg::StateAttribute::CULLFACE);
if (attribute)
{
assert(dynamic_cast<const osg::CullFace*>(attribute));
osg::CullFace::Mode mode = static_cast<const
osg::CullFace*>(attribute)->getMode();
if (mode == osg::CullFace::FRONT)
{
osg::notify(osg::WARN) << "FBX Writer: Reversed face (culled
FRONT) not supported yet." << std::endl;
}
else if (mode != osg::CullFace::BACK)
{
assert(mode == osg::CullFace::FRONT_AND_BACK);
osg::notify(osg::WARN) << "FBX Writer: Invisible face (culled
FRONT_AND_BACK) not supported yet." << std::endl;
}
}
_fbxMaterial = KFbxSurfacePhong::Create(pSdkManager,
mat->getName().c_str());
if (_fbxMaterial)
{
_fbxMaterial->GetDiffuseFactor().Set(1, true);
_fbxMaterial->GetDiffuseColor().Set(fbxDouble3(
diffuse.x(),
diffuse.y(),
diffuse.z()));
_fbxMaterial->GetTransparencyFactor().Set(transparency);
_fbxMaterial->GetAmbientColor().Set(fbxDouble3(
ambient.x(),
ambient.y(),
ambient.z()));
_fbxMaterial->GetEmissiveColor().Set(fbxDouble3(
emission.x(),
emission.y(),
emission.z()));
_fbxMaterial->GetSpecularColor().Set(fbxDouble3(
specular.x(),
specular.y(),
specular.z()));
_fbxMaterial->GetShininess().Set(shininess);
}
}
if (tex && tex->getImage(0))
{
_osgImage = tex->getImage(0);
ImageSet::iterator it = imageSet.find(_osgImage);
if (it == imageSet.end())
{
std::string canonicalPath(
osgDB::getRealPath(osgDB::convertFileNameToNativeStyle(_osgImage->getFileName()))
);
std::string destPath;
std::string relativePath;
if (canonicalPath.empty())
{
static const unsigned int MAX_IMAGE_NUMBER = UINT_MAX-1;
// -1 to allow doing +1 without an overflow
unsigned int imageNumber;
for (imageNumber=lastGeneratedImageFileName+1;
imageNumber<MAX_IMAGE_NUMBER; ++imageNumber)
{
std::ostringstream oss;
oss << "Image_" << imageNumber << ".tga";
relativePath = oss.str();
destPath = osgDB::concatPaths(_directory, relativePath);
if (imageFilenameSet.find(destPath) !=
imageFilenameSet.end()) break;
}
lastGeneratedImageFileName = imageNumber;
osgDB::writeImageFile(*_osgImage, destPath);
}
else
{
relativePath = getPathRelative(srcDirectory, canonicalPath);
destPath =
osgDB::getRealPath(osgDB::convertFileNameToNativeStyle(
osgDB::concatPaths(_directory, relativePath) ));
if (destPath != canonicalPath)
{
if (!osgDB::makeDirectoryForFile(destPath))
{
osg::notify(osg::NOTICE) << "Can't create directory for
file '" << destPath << "'. May fail creating the image file." << std::endl;
}
osgDB::writeImageFile(*_osgImage, destPath);
}
}
assert(!destPath.empty()); // Else the implementation is to
be fixed
assert(!relativePath.empty()); // ditto
it = imageSet.insert(ImageSet::value_type(_osgImage,
relativePath)).first;
imageFilenameSet.insert(destPath);
}
_fbxTexture = KFbxTexture::Create(pSdkManager, it->second.c_str());
_fbxTexture->SetFileName(it->second.c_str());
}
}
int WriterNodeVisitor::processStateSet(const osg::StateSet* ss)
{
//osg::notify(osg::ALWAYS) << "Trying Adding " <<
ss->getAttribute(osg::StateAttribute::MATERIAL)->getName() << std::endl;
MaterialMap::iterator itr = _materialMap.find(MaterialMap::key_type(ss));
if (itr != _materialMap.end())
{
if (itr->second.getIndex() < 0)
itr->second.setIndex(_lastMaterialIndex++);
return itr->second.getIndex();
}
const osg::Material* mat = dynamic_cast<const
osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
const osg::Texture* tex = dynamic_cast<const
osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
if (mat || tex)
{
int matNum = _lastMaterialIndex;
_materialMap.insert(MaterialMap::value_type(MaterialMap::key_type(ss),
Material(*this, _srcDirectory, ss, mat, tex, _pSdkManager,
_directory, _imageSet, _imageFilenameSet, _lastGeneratedImageFileName,
matNum)));
++_lastMaterialIndex;
return matNum;
}
return -1;
}
unsigned int addPolygon(MapIndices & index_vert, unsigned int vertIndex,
unsigned int normIndex, unsigned int drawableNum)
{
VertexIndex vert(vertIndex, drawableNum, normIndex);
MapIndices::iterator itIndex = index_vert.find(vert);
if (itIndex == index_vert.end())
{
unsigned int indexMesh = index_vert.size();
index_vert.insert(std::make_pair(vert, indexMesh));
return indexMesh;
}
return itIndex->second;
}
void addPolygon(KFbxMesh * mesh, MapIndices & index_vert, const Triangle & tri,
unsigned int drawableNum)
{
mesh->AddPolygon(addPolygon(index_vert, tri.t1, tri.normalIndex1,
drawableNum));
mesh->AddPolygon(addPolygon(index_vert, tri.t2, tri.normalIndex2,
drawableNum));
mesh->AddPolygon(addPolygon(index_vert, tri.t3, tri.normalIndex3,
drawableNum));
}
void
WriterNodeVisitor::setLayerTextureAndMaterial(KFbxMesh* mesh)
{
KFbxLayerElementTexture* lTextureDiffuseLayer =
KFbxLayerElementTexture::Create(mesh, "Diffuse");
lTextureDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON);
lTextureDiffuseLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT);
KFbxLayerElementMaterial* lMaterialLayer =
KFbxLayerElementMaterial::Create(mesh, "materialLayer");
lMaterialLayer->SetMappingMode(KFbxLayerElement::eBY_POLYGON);
lMaterialLayer->SetReferenceMode(KFbxLayerElement::eINDEX_TO_DIRECT);
lTextureDiffuseLayer->GetDirectArray().SetCount(_lastMaterialIndex);
lMaterialLayer->GetDirectArray().SetCount(_lastMaterialIndex);
for (MaterialMap::iterator it = _materialMap.begin(); it !=
_materialMap.end(); ++it)
{
if (it->second.getIndex() != -1)
{
KFbxSurfaceMaterial* lMaterial = it->second.getFbxMaterial();
KFbxTexture* lTexture = it->second.getFbxTexture();
lTextureDiffuseLayer->GetDirectArray().SetAt(it->second.getIndex(),
lTexture);
lMaterialLayer->GetDirectArray().SetAt(it->second.getIndex(),
lMaterial);
}
}
mesh->GetLayer(0)->SetMaterials(lMaterialLayer);
mesh->GetLayer(0)->SetTextures(KFbxLayerElement::eDIFFUSE_TEXTURES,
lTextureDiffuseLayer);
}
void
WriterNodeVisitor::setControlPointAndNormalsAndUV(const osg::Geode& geo,
MapIndices& index_vert,
bool texcoords,
KFbxMesh* mesh)
{
mesh->InitControlPoints(index_vert.size());
KFbxLayerElementNormal* lLayerElementNormal=
KFbxLayerElementNormal::Create(mesh, "");
// For now, FBX writer only supports normals bound per vertices
lLayerElementNormal->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT);
lLayerElementNormal->SetReferenceMode(KFbxLayerElement::eDIRECT);
lLayerElementNormal->GetDirectArray().SetCount(index_vert.size());
mesh->GetLayer(0)->SetNormals(lLayerElementNormal);
KFbxLayerElementUV* lUVDiffuseLayer = KFbxLayerElementUV::Create(mesh,
"DiffuseUV");
if (texcoords)
{
lUVDiffuseLayer->SetMappingMode(KFbxLayerElement::eBY_CONTROL_POINT);
lUVDiffuseLayer->SetReferenceMode(KFbxLayerElement::eDIRECT);
lUVDiffuseLayer->GetDirectArray().SetCount(index_vert.size());
mesh->GetLayer(0)->SetUVs(lUVDiffuseLayer,
KFbxLayerElement::eDIFFUSE_TEXTURES);
}
for (MapIndices::iterator it = index_vert.begin(); it != index_vert.end();
++it)
{
const osg::Geometry* pGeometry =
geo.getDrawable(it->first.drawableIndex)->asGeometry();
unsigned int vertexIndex = it->first.vertexIndex;
unsigned int normalIndex = it->first.normalIndex;
const osg::Array * basevecs = pGeometry->getVertexArray();
assert(basevecs);
if (!basevecs || basevecs->getNumElements()==0)
{
//OSG_NOTIFY()
continue;
}
KFbxVector4 vertex;
if (basevecs->getType() == osg::Array::Vec3ArrayType)
{
const osg::Vec3 & vec = (*static_cast<const osg::Vec3Array
*>(basevecs))[vertexIndex];
// Sukender: "*new KFbxVector4"? Shouldn't it be "KFbxVector4"
alone?
//mesh->SetControlPointAt(*new KFbxVector4(vec.x(), vec.y(),
vec.z()), it->second);
vertex.Set(vec.x(), vec.y(), vec.z());
}
else if (basevecs->getType() == osg::Array::Vec3dArrayType)
{
const osg::Vec3d & vec = (*static_cast<const osg::Vec3dArray
*>(basevecs))[vertexIndex];
// Sukender: "*new KFbxVector4"? Shouldn't it be "KFbxVector4"
alone?
//mesh->SetControlPointAt(*new KFbxVector4(vec.x(), vec.y(),
vec.z()), it->second);
vertex.Set(vec.x(), vec.y(), vec.z());
}
else
{
OSG_NOTIFY(osg::FATAL) << "Vertex array is not Vec3 or Vec3d. Not
implemented" << std::endl;
throw "Vertex array is not Vec3 or Vec3d. Not implemented";
//_succeeded = false;
//return;
}
mesh->SetControlPointAt(vertex, it->second);
const osg::Array * basenormals = pGeometry->getNormalArray();
if (basenormals && basenormals->getNumElements()>0)
{
KFbxVector4 normal;
if (basenormals->getType() == osg::Array::Vec3ArrayType)
{
const osg::Vec3 & vec = (*static_cast<const osg::Vec3Array
*>(basenormals))[normalIndex];
normal.Set(vec.x(), vec.y(), vec.z(), 0);
}
else if (basenormals->getType() == osg::Array::Vec3dArrayType)
{
const osg::Vec3d & vec = (*static_cast<const osg::Vec3dArray
*>(basenormals))[normalIndex];
normal.Set(vec.x(), vec.y(), vec.z(), 0);
}
else
{
OSG_NOTIFY(osg::FATAL) << "Normal array is not Vec3 or Vec3d.
Not implemented" << std::endl;
throw "Normal array is not Vec3 or Vec3d. Not implemented";
//_succeeded = false;
//return;
}
//switch (pGeometry->getNormalBinding())
//{
//case osg::Geometry::BIND_PER_PRIMITIVE_SET:
//case osg::Geometry::BIND_PER_PRIMITIVE:
//case osg::Geometry::BIND_PER_VERTEX:
// break;
//}
lLayerElementNormal->GetDirectArray().SetAt(it->second, normal);
}
if (texcoords)
{
const osg::Array * basetexcoords = pGeometry->getTexCoordArray(0);
if (basetexcoords && basetexcoords->getNumElements()>0)
{
KFbxVector2 texcoord;
if (basetexcoords->getType() == osg::Array::Vec2ArrayType)
{
const osg::Vec2 & vec = (*static_cast<const osg::Vec2Array
*>(basetexcoords))[vertexIndex];
texcoord.Set(vec.x(), vec.y());
}
else if (basetexcoords->getType() == osg::Array::Vec2dArrayType)
{
const osg::Vec2d & vec = (*static_cast<const
osg::Vec2dArray *>(basetexcoords))[vertexIndex];
texcoord.Set(vec.x(), vec.y());
}
else
{
OSG_NOTIFY(osg::FATAL) << "Texture coords array is not Vec2
or Vec2d. Not implemented" << std::endl;
throw "Texture coords array is not Vec2 or Vec2d. Not
implemented";
//_succeeded = false;
//return;
}
lUVDiffuseLayer->GetDirectArray().SetAt(it->second, texcoord);
}
}
}
}
void WriterNodeVisitor::buildFaces(const osg::Geode& geo,
ListTriangle& listTriangles,
bool texcoords)
{
MapIndices index_vert;
KFbxMesh* mesh = KFbxMesh::Create(_pSdkManager, geo.getName().c_str());
_curFbxNode->AddNodeAttribute(mesh);
_curFbxNode->SetShadingMode(KFbxNode::eTEXTURE_SHADING);
KFbxLayer* lLayer = mesh->GetLayer(0);
if (lLayer == NULL)
{
mesh->CreateLayer();
lLayer = mesh->GetLayer(0);
}
setLayerTextureAndMaterial(mesh);
lLayer->GetTextures(KFbxLayerElement::eDIFFUSE_TEXTURES)->GetIndexArray().SetCount(listTriangles.size());
lLayer->GetMaterials()->GetIndexArray().SetCount(listTriangles.size());
unsigned int i = 0;
for (ListTriangle::iterator it = listTriangles.begin(); it !=
listTriangles.end(); ++it, ++i) //Go through the triangle list to define meshs
{
if (it->first.material == -1)
{
mesh->BeginPolygon();
}
else
{
mesh->BeginPolygon(i);
lLayer->GetTextures(KFbxLayerElement::eDIFFUSE_TEXTURES)->GetIndexArray().SetAt(i,
it->first.material);
lLayer->GetMaterials()->GetIndexArray().SetAt(i,
it->first.material);
}
addPolygon(mesh, index_vert, it->first, it->second);
mesh->EndPolygon();
}
setControlPointAndNormalsAndUV(geo, index_vert, texcoords, mesh);
}
void WriterNodeVisitor::createListTriangle(const osg::Geometry* geo,
ListTriangle& listTriangles,
bool& texcoords,
unsigned int& drawable_n)
{
unsigned int nbVertices = 0;
texcoords = false;
{
const osg::Array * vecs = geo->getVertexArray();
if (vecs)
{
nbVertices = vecs->getNumElements();
// Texture coords
const osg::Array * texvec = geo->getTexCoordArray(0);
if (texvec)
{
unsigned int nb = texvec->getNumElements();
if (nb == nbVertices) texcoords = true;
else
{
OSG_NOTIFY(osg::WARN) << "There are more/less texture
coords than vertices! Ignoring texture coords.";
}
}
}
}
if (nbVertices==0) return;
int material = processStateSet(_currentStateSet.get());
PrimitiveIndexWriter pif(geo, listTriangles, drawable_n, material);
for (unsigned int iPrimSet = 0; iPrimSet < geo->getNumPrimitiveSets();
++iPrimSet) //Fill the Triangle List
{
const osg::PrimitiveSet* ps = geo->getPrimitiveSet(iPrimSet);
const_cast<osg::PrimitiveSet*>(ps)->accept(pif);
}
}
void WriterNodeVisitor::apply(osg::Geode& node)
{
KFbxNode* parent = _curFbxNode;
KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ?
"DefaultName" : node.getName().c_str());
_curFbxNode->AddChild(nodeFBX);
_curFbxNode = nodeFBX;
unsigned int count = node.getNumDrawables();
ListTriangle listTriangles;
bool texcoords = false;
for (MaterialMap::iterator it = _materialMap.begin(); it !=
_materialMap.end(); ++it)
it->second.setIndex(-1);
_lastMaterialIndex = 0;
if(node.getStateSet()){
pushStateSet(node.getStateSet());
}
for (unsigned int i = 0; i < count; ++i)
{
const osg::Geometry* g = node.getDrawable(i)->asGeometry();
if (g != NULL)
{
pushStateSet(g->getStateSet());
createListTriangle(g, listTriangles, texcoords, i);
popStateSet(g->getStateSet());
}
}
if(node.getStateSet()){
popStateSet(node.getStateSet());
}
if (count > 0)
{
buildFaces(node, listTriangles, texcoords);
}
if (succeedLastApply())
traverse(node);
_curFbxNode = parent;
}
void WriterNodeVisitor::apply(osg::Group& node)
{
KFbxNode* parent = _curFbxNode;
KFbxNode* nodeFBX = KFbxNode::Create(_pSdkManager, node.getName().empty() ?
"DefaultName" : node.getName().c_str());
_curFbxNode->AddChild(nodeFBX);
_curFbxNode = nodeFBX;
traverse(node);
_curFbxNode = parent;
}
void WriterNodeVisitor::apply(osg::MatrixTransform& node)
{
KFbxNode* parent = _curFbxNode;
_curFbxNode = KFbxNode::Create(_pSdkManager, node.getName().empty() ?
"DefaultName" : node.getName().c_str());
parent->AddChild(_curFbxNode);
const osg::Matrix& matrix = node.getMatrix();
osg::Vec3d pos, scl;
osg::Quat rot, so;
matrix.decompose(pos, rot, scl, so);
_curFbxNode->LclTranslation.Set(fbxDouble3(pos.x(), pos.y(), pos.z()));
_curFbxNode->LclScaling.Set(fbxDouble3(scl.x(), scl.y(), scl.z()));
KFbxXMatrix mat;
KFbxQuaternion q(rot.x(), rot.y(), rot.z(), rot.w());
mat.SetQ(q);
KFbxVector4 vec4 = mat.GetR();
_curFbxNode->LclRotation.Set(fbxDouble3(vec4[0], vec4[1], vec4[2]));
traverse(node);
_curFbxNode = parent;
}
// end namespace pluginfbx
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org