Hi Robert,
Here are two sumbissions for DAE.
1. daeRGeometry.cpp (rev 12132). A tiny sumbission which fixes a quite
important bug: a parameter was forgotten in Collada ReaderWriter, and texture
coordinates could not be loaded properly. So:
"
Added missing paramter when calling createGeometryData(). Fixes missing texture
coordinates (in "bind_vertex_input").
"
2. ReaderWriterDAE.cpp (rev 12132):
"
Added "baseImageDir" as a plugin string data, in order to manually specify base
directory to use when relativising image file names. This is used to properly
write files, when images are not located in a subdirectory (like "../images"
for some software).
Also added a comment to warn about writing a const object.
"
Cheers,
Sukender
PVLE - Lightweight cross-platform game engine - http://pvle.sourceforge.net/
/*
* Copyright 2006 Sony Computer Entertainment Inc.
*
* Licensed under the SCEA Shared Source License, Version 1.0 (the "License");
you may not use this
* file except in compliance with the License. You may obtain a copy of the
License at:
* http://research.scea.com/scea_shared_source_license.html
*
* Unless required by applicable law or agreed to in writing, software
distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or
* implied. See the License for the specific language governing permissions and
limitations under the
* License.
*/
#include "daeReader.h"
#include "domSourceReader.h"
#include <dae.h>
#include <dom/domCOLLADA.h>
#include <dom/domInstance_geometry.h>
#include <dom/domInstance_controller.h>
#include <dom/domController.h>
#include <dom/domConstants.h>
#include <osg/StateSet>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osgAnimation/MorphGeometry>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/UpdateBone>
using namespace osgDAE;
osg::Geode* daeReader::getOrCreateGeometry(domGeometry *pDomGeometry,
domBind_material* pDomBindMaterial, const osg::Geode** ppOriginalGeode)
{
// Check cache if geometry already exists
osg::Geode* pOsgGeode;
domGeometryGeodeMap::iterator iter = _geometryMap.find( pDomGeometry );
if ( iter != _geometryMap.end() )
{
pOsgGeode = iter->second.get();
}
else
{
pOsgGeode = processGeometry( pDomGeometry );
_geometryMap.insert( std::make_pair( pDomGeometry, pOsgGeode ) );
}
if (ppOriginalGeode)
{
*ppOriginalGeode = pOsgGeode;
}
if (!pOsgGeode)
return NULL;
// Create a copy of the cached Geode with a copy of the drawables,
// because we may be using a different material or texture unit bindings.
osg::Geode *pCopiedOsgGeode =
static_cast<osg::Geode*>(pOsgGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES));
if ( pCopiedOsgGeode == NULL )
{
OSG_WARN << "Failed to load geometry " << pDomGeometry->getName() <<
std::endl;
return NULL;
}
// Compute optimized geometry by expanding all indexed arrays so we are no
longer rendering with the slow path
for(unsigned int i=0;i < pCopiedOsgGeode->getNumDrawables();++i)
{
osg::Geometry* geom = pCopiedOsgGeode->getDrawable(i)->asGeometry();
if (geom)
{
if (!geom->areFastPathsUsed() &&
!geom->getInternalOptimizedGeometry())
{
//geom->computeInternalOptimizedGeometry();
}
}
}
if (pDomBindMaterial)
{
processBindMaterial( pDomBindMaterial, pDomGeometry, pCopiedOsgGeode,
pOsgGeode );
}
return pCopiedOsgGeode;
}
osgAnimation::Bone* daeReader::getOrCreateBone(domNode *pDomNode)
{
// Check cache if bone already exists
osgAnimation::Bone *pOsgBone = NULL;
domNodeOsgBoneMap::iterator iterBone = _jointMap.find( pDomNode );
if ( iterBone != _jointMap.end() )
return iterBone->second.get();
std::string name;
if (pDomNode->getId())
name = pDomNode->getId();
if (name.empty() && pDomNode->getSid())
name = pDomNode->getSid();
if (name.empty() && pDomNode->getName())
name = pDomNode->getName();
pOsgBone = new osgAnimation::Bone(name);
pOsgBone->setDataVariance(osg::Object::DYNAMIC);
pOsgBone->setUpdateCallback(new osgAnimation::UpdateBone(name));
_jointMap.insert( std::make_pair( pDomNode, pOsgBone ) );
return pOsgBone;
}
osgAnimation::Skeleton* daeReader::getOrCreateSkeleton(domNode *pDomNode)
{
// Check cache if skeleton already exists
osgAnimation::Skeleton *pOsgSkeleton = NULL;
domNodeOsgSkeletonMap::iterator iter = _skeletonMap.find( pDomNode );
if ( iter != _skeletonMap.end() )
return iter->second.get();
pOsgSkeleton = new osgAnimation::Skeleton;
pOsgSkeleton->setDefaultUpdateCallback();
pOsgSkeleton->setDataVariance(osg::Object::DYNAMIC);
_skeletonMap.insert( std::make_pair( pDomNode, pOsgSkeleton ) );
return pOsgSkeleton;
}
osg::Geode* daeReader::processInstanceGeometry( domInstance_geometry
*pDomInstanceGeometry )
{
domGeometry *pDomGeometry = daeSafeCast< domGeometry
>(getElementFromURI(pDomInstanceGeometry->getUrl()));
if (!pDomGeometry)
{
OSG_WARN << "Failed to locate geometry " <<
pDomInstanceGeometry->getUrl().getURI() << std::endl;
return NULL;
}
return getOrCreateGeometry(pDomGeometry,
pDomInstanceGeometry->getBind_material());
}
// <morph source (method)>
// 2..* <source>
// 1 <targets>
// 2..* <input semantic source>
// 0..* <extra>
// 0..* <extra>
osg::Node* daeReader::processMorph(domMorph* pDomMorph, domBind_material*
pDomBindMaterial)
{
domGeometry* pDomGeometry = daeSafeCast< domGeometry >(getElementFromURI(
pDomMorph->getSource()));
if (!pDomGeometry)
{
OSG_WARN << "Failed to locate geometry " <<
pDomMorph->getSource().getURI() << std::endl;
return NULL;
}
// Base mesh
osg::Geode* pOsgGeode = getOrCreateGeometry(pDomGeometry, pDomBindMaterial);
if (!pOsgGeode)
return NULL;
// Expects a single geometry inside the geode, should change this
osg::Geometry* pOsgGeometry =
dynamic_cast<osg::Geometry*>(pOsgGeode->getDrawable(0));
if (!pOsgGeometry)
return NULL;
osgAnimation::MorphGeometry* pOsgMorphGeometry = new
osgAnimation::MorphGeometry(*pOsgGeometry);
pOsgGeode->removeDrawables(0);
pOsgGeode->addDrawable(pOsgMorphGeometry);
domMorphMethodType morphMethod = pDomMorph->getMethod();
//Files exported by the FBX converter always seem to say they're relative
//when in fact they should be normalized.
if (_authoringTool == FBX_CONVERTER)
{
morphMethod = MORPHMETHODTYPE_NORMALIZED;
}
switch (morphMethod)
{
case MORPHMETHODTYPE_RELATIVE:
pOsgMorphGeometry->setMethod(osgAnimation::MorphGeometry::RELATIVE);
break;
case MORPHMETHODTYPE_NORMALIZED:
pOsgMorphGeometry->setMethod(osgAnimation::MorphGeometry::NORMALIZED);
break;
default:
OSG_WARN << "Unknown morph method method type " << std::endl;
}
// 1 <targets>
domMorph::domTargets* pDomMorhpTargets = pDomMorph->getTargets();
domInputLocal_Array domInputs = pDomMorhpTargets->getInput_array();
// TODO how to handle multiple pairs of morph inputs?
if (domInputs.getCount() > 2)
{
OSG_WARN << "Only a single pair of morph inputs is supported." <<
std::endl;
}
for (size_t i=0; i < 2; i++)
{
if (!strcmp(domInputs[i]->getSemantic(),
COMMON_PROFILE_INPUT_MORPH_TARGET))
{
domSource* pDomSource =
daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (pDomSource)
{
if (const domName_array* pDomNames =
pDomSource->getName_array())
{
const domListOfNames& names = pDomNames->getValue();
for (size_t j=0; j < names.getCount(); j++)
{
daeSIDResolver resolver(_visualScene, names.get(j));
pDomGeometry = daeSafeCast< domGeometry
>(resolver.getElement());
if (pDomGeometry)
{
osg::Geode* targetgeode =
getOrCreateGeometry(pDomGeometry, NULL);
// Expects a single geometry inside the geode,
should change this
osg::Geometry* pOsgGeometry =
dynamic_cast<osg::Geometry*>(targetgeode->getDrawable(0));
if (pOsgGeometry)
{
pOsgMorphGeometry->addMorphTarget(pOsgGeometry);
}
}
else
{
OSG_WARN << "Failed to locate morph geometry '" <<
names.get(j) << "'" << std::endl;
}
}
}
else if (domIDREF_array* pDomIDREFs =
pDomSource->getIDREF_array())
{
xsIDREFS* pIDREFS = &(pDomIDREFs->getValue());
for (size_t j=0; j < pIDREFS->getCount(); j++)
{
pDomGeometry = daeSafeCast< domGeometry
>(getElementFromIDRef(pIDREFS->get(j)));
if (pDomGeometry)
{
osg::Geode* targetgeode =
getOrCreateGeometry(pDomGeometry, NULL);
// Expects a single geometry inside the geode,
should change this
osg::Geometry* pOsgGeometry =
dynamic_cast<osg::Geometry*>(targetgeode->getDrawable(0));
if (pOsgGeometry)
{
pOsgMorphGeometry->addMorphTarget(pOsgGeometry);
}
}
else
{
OSG_WARN << "Failed to locate morph geometry '" <<
pIDREFS->get(j).getID() << "'" << std::endl;
}
}
}
}
else
{
OSG_WARN << "Could not find morph source '" <<
domInputs[i]->getSource().getURI() << "'" <<std::endl;
return NULL;
}
}
else if (!strcmp(domInputs[i]->getSemantic(),
COMMON_PROFILE_INPUT_MORPH_WEIGHT))
{
domSource* pDomSource =
daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (pDomSource)
{
domFloat_array* pDomFloatArray = pDomSource->getFloat_array();
domListOfFloats weights = pDomFloatArray->getValue();
for (size_t j=0; j < pDomFloatArray->getCount(); j++)
{
pOsgMorphGeometry->setWeight(j, weights.get(j));
}
// See if morph weights are targetted by animations
daeElementDomChannelMap::iterator iter =
_daeElementDomChannelMap.find(pDomSource);
if (iter != _daeElementDomChannelMap.end())
{
std::string name = pDomSource->getId() ?
pDomSource->getId() : "";
osgAnimation::UpdateMorph* pUpdateCallback = new
osgAnimation::UpdateMorph(name);
pOsgGeode->setUpdateCallback(pUpdateCallback);
pOsgGeode->setDataVariance(osg::Object::DYNAMIC);
// Associate all animation channels with this update
callback
do
{
_domChannelOsgAnimationUpdateCallbackMap[iter->second]
= pUpdateCallback;
++iter;
}
while (iter !=
_daeElementDomChannelMap.upper_bound(pDomSource));
}
}
else
{
OSG_WARN << "Could not find morph source '" <<
domInputs[i]->getSource().getURI() << "'" <<std::endl;
return NULL;
}
}
}
return pOsgGeode;
}
// <controller (id) (name)>
// 0..1 <asset>
// 1 <skin>, <morph>
// 0..* <extra>
osg::Node* daeReader::processInstanceController( domInstance_controller
*pDomInstanceController )
{
domController *pDomController = daeSafeCast< domController
>(getElementFromURI(pDomInstanceController->getUrl()));
if (!pDomController)
{
OSG_WARN << "Failed to locate controller " <<
pDomInstanceController->getUrl().getURI() << std::endl;
return NULL;
}
if (pDomController->getSkin())
{
_skinInstanceControllers.push_back(pDomInstanceController);
return NULL;
}
else if (pDomController->getMorph())
{
return processMorph(pDomController->getMorph(),
pDomInstanceController->getBind_material());
}
OSG_WARN << "Expected skin or morph element in controller '" <<
pDomController->getName() << "'" << std::endl;
return NULL;
}
// <mesh>
// 1..* <source>
// 1 <vertices>
// 0..* <lines>, <linestrips>, <polygons>, <polylist>, <triangles>,
<trifans>, <tristrips>
// 0..* <extra>
osg::Geode *daeReader::processMesh(domMesh* pDomMesh)
{
osg::Geode* pOsgGeode = new osg::Geode;
// if (pDomMesh->getId() != NULL )
{
// pOsgGeode->setName( pDomMesh->getId() );
}
// size_t count = mesh->getContents().getCount();
// 1..* <source>
SourceMap sources;
domSource_Array sourceArray = pDomMesh->getSource_array();
for ( size_t i = 0; i < sourceArray.getCount(); i++)
{
sources.insert(std::make_pair((daeElement*)sourceArray[i],
domSourceReader(sourceArray[i])));
}
// 0..* <lines>
domLines_Array linesArray = pDomMesh->getLines_array();
for ( size_t i = 0; i < linesArray.getCount(); i++)
{
processSinglePPrimitive<domLines>(pOsgGeode, pDomMesh, linesArray[i],
sources, GL_LINES);
}
// 0..* <linestrips>
domLinestrips_Array linestripsArray = pDomMesh->getLinestrips_array();
for ( size_t i = 0; i < linestripsArray.getCount(); i++)
{
processMultiPPrimitive<domLinestrips>(pOsgGeode, pDomMesh,
linestripsArray[i], sources, GL_LINE_STRIP);
}
// 0..* <polygons>
domPolygons_Array polygonsArray = pDomMesh->getPolygons_array();
for ( size_t i = 0; i < polygonsArray.getCount(); i++)
{
processPolygons<domPolygons>(pOsgGeode, pDomMesh, polygonsArray[i],
sources);
}
// 0..* <polylist>
domPolylist_Array polylistArray = pDomMesh->getPolylist_array();
for ( size_t i = 0; i < polylistArray.getCount(); i++)
{
processPolylist(pOsgGeode, pDomMesh, polylistArray[i], sources);
}
// 0..* <triangles>
domTriangles_Array trianglesArray = pDomMesh->getTriangles_array();
for ( size_t i = 0; i < trianglesArray.getCount(); i++)
{
processSinglePPrimitive<domTriangles>(pOsgGeode, pDomMesh,
trianglesArray[i], sources, GL_TRIANGLES);
}
// 0..* <trifans>
domTrifans_Array trifansArray = pDomMesh->getTrifans_array();
for ( size_t i = 0; i < trifansArray.getCount(); i++)
{
processPolygons<domTrifans>(pOsgGeode, pDomMesh, trifansArray[i],
sources);
}
// 0..* <tristrips>
domTristrips_Array tristripsArray = pDomMesh->getTristrips_array();
for ( size_t i = 0; i < tristripsArray.getCount(); i++)
{
processMultiPPrimitive<domTristrips>(pOsgGeode, pDomMesh,
tristripsArray[i], sources, GL_TRIANGLE_STRIP);
}
return pOsgGeode;
}
// <convexmesh>
osg::Geode *daeReader::processConvexMesh(domConvex_mesh* pDomConvexMesh)
{
// OSG_WARN << "Unsupported geometry convex mesh '" <<
pDomConvexMesh->getId() << "'" << std::endl;
return NULL;
}
// <spline>
osg::Geode *daeReader::processSpline(domSpline* pDomSpline)
{
// OSG_WARN << "Unsupported geometry type spline '" << pDomSpline->getId()
<< "'" << std::endl;
return NULL;
}
// <geometry (id) (name)>
// 0..1 <asset>
// 1 <convex_mesh>, <mesh>, <spline>
// 0..* <extra>
osg::Geode *daeReader::processGeometry(domGeometry *pDomGeometry)
{
if (pDomGeometry->getMesh())
{
return processMesh(pDomGeometry->getMesh());
}
else if (pDomGeometry->getConvex_mesh())
{
return processConvexMesh(pDomGeometry->getConvex_mesh());
}
else if (pDomGeometry->getSpline())
{
return processSpline(pDomGeometry->getSpline());
}
#ifdef COLLADA15
else if (pDomGeometry->getBRep())
{
return processBRep(pDomGeometry->getBRep());
}
#endif
OSG_WARN << "Unexpected geometry type in geometry '" <<
pDomGeometry->getId() << "'" << std::endl;
return NULL;
}
template< typename T >
void daeReader::processSinglePPrimitive(osg::Geode* geode,
const domMesh* pDomMesh, const T* group, SourceMap& sources, GLenum mode)
{
osg::Geometry *geometry = new osg::Geometry();
if (NULL != group->getMaterial())
geometry->setName(group->getMaterial());
geode->addDrawable( geometry );
osg::DrawElementsUInt* pDrawElements = new osg::DrawElementsUInt(mode);
geometry->addPrimitiveSet(pDrawElements);
domP_Array domPArray;
domPArray.append(group->getP());
std::vector<std::vector<GLuint> > indexLists;
resolveMeshArrays(domPArray, group->getInput_array(), pDomMesh,
geometry, sources, indexLists);
pDrawElements->asVector().swap(indexLists.front());
}
template< typename T >
void daeReader::processMultiPPrimitive(osg::Geode* geode,
const domMesh* pDomMesh, const T* group, SourceMap &sources, GLenum mode)
{
osg::Geometry *geometry = new osg::Geometry();
if (NULL != group->getMaterial())
geometry->setName(group->getMaterial());
geode->addDrawable( geometry );
std::vector<std::vector<GLuint> > indexLists;
resolveMeshArrays(group->getP_array(), group->getInput_array(), pDomMesh,
geometry, sources, indexLists);
for (size_t i = 0; i < indexLists.size(); ++i)
{
osg::DrawElementsUInt* pDrawElements = new osg::DrawElementsUInt(mode);
geometry->addPrimitiveSet(pDrawElements);
pDrawElements->asVector().swap(indexLists[i]);
}
}
void daeReader::processPolylist(osg::Geode* geode, const domMesh* pDomMesh,
const domPolylist *group, SourceMap &sources)
{
const domPolylist::domVcount* pDomVcount = group->getVcount();
if (!pDomVcount)
{
OSG_WARN << "Index counts not found." << std::endl;
return;
}
osg::Geometry* geometry = new osg::Geometry();
if (NULL != group->getMaterial())
geometry->setName(group->getMaterial());
geode->addDrawable(geometry);
std::vector<std::vector<GLuint> > vertexLists;
domP_Array domPArray;
domPArray.append(group->getP());
resolveMeshArrays(domPArray, group->getInput_array(), pDomMesh, geometry,
sources, vertexLists);
const std::vector<GLuint>& vertexList = vertexLists.front();
osg::DrawElementsUInt* pDrawTriangles = new
osg::DrawElementsUInt(GL_TRIANGLES);
geometry->addPrimitiveSet(pDrawTriangles);
const domListOfUInts& vCount = pDomVcount->getValue();
for (size_t i = 0, j = 0; i < vCount.getCount(); ++i)
{
size_t primitiveLength = vCount[i];
if (j + primitiveLength > vertexList.size())
{
OSG_WARN << "Error: vertex counts are greater than the number of
indices." << std::endl;
return;
}
for (size_t k = 2; k < primitiveLength; ++k)
{
pDrawTriangles->push_back(vertexList[j]);
pDrawTriangles->push_back(vertexList[j+k-1]);
pDrawTriangles->push_back(vertexList[j+k]);
}
j += primitiveLength;
}
}
template <typename T>
void daeReader::processPolygons(osg::Geode* geode,
const domMesh* pDomMesh, const T *group, SourceMap& sources)
{
osg::Geometry *geometry = new osg::Geometry();
geometry->setName(group->getMaterial());
geode->addDrawable(geometry);
osg::DrawElementsUInt* pDrawElements = new
osg::DrawElementsUInt(GL_TRIANGLES);
geometry->addPrimitiveSet(pDrawElements);
std::vector<std::vector<GLuint> > indexLists;
resolveMeshArrays(group->getP_array(), group->getInput_array(), pDomMesh,
geometry, sources, indexLists);
for ( size_t i = 0; i < indexLists.size(); ++i)
{
const std::vector<GLuint>& indices = indexLists[i];
for (size_t j = 2; j < indices.size(); ++j)
{
pDrawElements->push_back(indices.front());
pDrawElements->push_back(indices[j - 1]);
pDrawElements->push_back(indices[j]);
}
}
}
void processVertices(
domVertices* vertices,
daeElement*& position_source,
daeElement*& color_source,
daeElement*& normal_source,
daeElement*& texcoord_source)
{
const domInputLocal_Array& inputs = vertices->getInput_array();
// Process input elements within the vertices element. These are of the
unshared type
// and therefore cannot have set and offset attributes
for (size_t i = 0; i < inputs.getCount(); ++i)
{
xsNMTOKEN semantic = inputs[i]->getSemantic();
daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource());
if (strcmp(COMMON_PROFILE_INPUT_POSITION, semantic) == 0)
{
position_source = pDaeElement;
}
else if (strcmp(COMMON_PROFILE_INPUT_COLOR, semantic) == 0)
{
color_source = pDaeElement;
}
else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, semantic) == 0)
{
normal_source = pDaeElement;
}
else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, semantic) == 0)
{
texcoord_source = pDaeElement;
}
}
}
// I've never seen more than 2 used so this should be enough. If you find that
// a greater number is needed then increase it accordingly and submit the change
// to OpenSceneGraph.
// Why not use a vector? Because a large map of VertexIndices is used and
// allocating vectors for each element would make it a lot slower.
const unsigned int MAX_TEXTURE_COORDINATE_SETS = 4;
void resolveMeshInputs(
const domInputLocalOffset_Array &inputs,
daeElement*& position_source,
daeElement*& color_source,
daeElement*& normal_source,
daeElement* texcoord_sources[MAX_TEXTURE_COORDINATE_SETS],
int& position_offset,
int& color_offset,
int& normal_offset,
int texcoord_offsets[MAX_TEXTURE_COORDINATE_SETS])
{
position_source = color_source = normal_source = NULL;
position_offset = color_offset = normal_offset = 0;
for (unsigned int i = 0; i < MAX_TEXTURE_COORDINATE_SETS; ++i)
{
texcoord_sources[i] = NULL;
texcoord_offsets[i] = NULL;
}
for ( size_t i = 0; i < inputs.getCount(); i++ )
{
if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0)
{
daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource());
if (domVertices* vertices = daeSafeCast<domVertices>(pDaeElement))
{
processVertices(vertices, position_source, color_source,
normal_source, texcoord_sources[0]);
position_offset = inputs[i]->getOffset();
if (color_source) color_offset = position_offset;
if (normal_source) normal_offset = position_offset;
if (texcoord_sources[0]) texcoord_offsets[0] = position_offset;
}
break;
}
}
for ( size_t i = 0; i < inputs.getCount(); i++ )
{
xsNMTOKEN semantic = inputs[i]->getSemantic();
daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource());
int offset = inputs[i]->getOffset();
if (strcmp(COMMON_PROFILE_INPUT_COLOR, semantic) == 0)
{
if (color_source != NULL)
OSG_WARN<<"Overwriting vertices input(COLOR) with input from
primitive"<<std::endl;
color_source = pDaeElement;
color_offset = offset;
}
else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, semantic) == 0)
{
if (normal_source != NULL)
OSG_WARN<<"Overwriting vertices input(NORMAL) with input from
primitive"<<std::endl;
normal_source = pDaeElement;
normal_offset = offset;
}
else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, semantic) == 0)
{
unsigned set = inputs[i]->getSet();
if (set >= MAX_TEXTURE_COORDINATE_SETS)
{
OSG_WARN<<"Texture coordinate set "<< set <<
"was requested, the maximum allowed is " <<
MAX_TEXTURE_COORDINATE_SETS - 1 << "." << std::endl;
continue;
}
if (texcoord_sources[set])
OSG_WARN<<"Overwriting vertices input(TEXCOORD) with input from
primitive"<<std::endl;
texcoord_sources[set] = pDaeElement;
texcoord_offsets[set] = offset;
}
}
}
struct VertexIndices
{
VertexIndices(int p, int c, int n, const int t[MAX_TEXTURE_COORDINATE_SETS])
: position_index(p), color_index(c), normal_index(n)
{
for (unsigned int i = 0; i < MAX_TEXTURE_COORDINATE_SETS; ++i)
texcoord_indices[i] = t[i];
}
bool operator < (const VertexIndices& rhs) const
{
if (position_index != rhs.position_index) return position_index <
rhs.position_index;
if (color_index != rhs.color_index) return color_index <
rhs.color_index;
if (normal_index != rhs.normal_index) return normal_index <
rhs.normal_index;
for (unsigned int i = 0; i < MAX_TEXTURE_COORDINATE_SETS; ++i)
{
if (texcoord_indices[i] != rhs.texcoord_indices[i]) return
texcoord_indices[i] < rhs.texcoord_indices[i];
}
return false;
}
/// Templated getter for memebers, used for createGeometryData()
enum ValueType { POSITION, COLOR, NORMAL, TEXCOORD };
template <int Value>
inline int get() const;
int position_index, color_index, normal_index,
texcoord_indices[MAX_TEXTURE_COORDINATE_SETS];
};
template<>
inline int VertexIndices::get<VertexIndices::POSITION>() const { return
position_index; }
template<>
inline int VertexIndices::get<VertexIndices::COLOR>() const { return
color_index; }
template<>
inline int VertexIndices::get<VertexIndices::NORMAL>() const { return
normal_index; }
template<int Value>
inline int VertexIndices::get() const {
// TEXCOORD has not to be implemented here as we need compile-time
constants for texcoord number
return -1;
}
typedef std::map<VertexIndices, GLuint> VertexIndicesIndexMap;
/// Creates a value array, packed in a osg::Geometry::ArrayData, corresponding
to indexed values.
template <class ArrayType, int Value>
osg::Geometry::ArrayData createGeometryData(domSourceReader & sourceReader,
const VertexIndicesIndexMap & vertexIndicesIndexMap, int texcoordNum=-1) {
const ArrayType * source = sourceReader.getArray<ArrayType>();
if (!source) return osg::Geometry::ArrayData();
ArrayType * pArray = new ArrayType;
for (VertexIndicesIndexMap::const_iterator it =
vertexIndicesIndexMap.begin(), end = vertexIndicesIndexMap.end(); it != end;
++it) {
int index = texcoordNum>=0 ? it->first.texcoord_indices[texcoordNum] :
it->first.get<Value>();
if (index>=0 && static_cast<unsigned int>(index)<source->size())
pArray->push_back(source->at(index));
else {
// Invalid data (index out of bounds)
//LOG_WARN << ...
//pArray->push_back(0);
return osg::Geometry::ArrayData();
}
}
return osg::Geometry::ArrayData(pArray, osg::Geometry::BIND_PER_VERTEX);
}
template <class ArrayTypeSingle, class ArrayTypeDouble, int Value>
inline osg::Geometry::ArrayData createGeometryData(domSourceReader &
sourceReader, const VertexIndicesIndexMap & vertexIndicesIndexMap, bool
useDoublePrecision, int texcoordNum=-1) {
if (useDoublePrecision) return createGeometryData<ArrayTypeDouble,
Value>(sourceReader, vertexIndicesIndexMap, texcoordNum);
else return createGeometryData<ArrayTypeSingle,
Value>(sourceReader, vertexIndicesIndexMap, texcoordNum);
}
void daeReader::resolveMeshArrays(const domP_Array& domPArray,
const domInputLocalOffset_Array& inputs, const domMesh* pDomMesh,
osg::Geometry* geometry, SourceMap &sources,
std::vector<std::vector<GLuint> >& vertexLists)
{
daeElement* position_source = NULL;
daeElement* color_source = NULL;
daeElement* normal_source = NULL;
daeElement* texcoord_sources[MAX_TEXTURE_COORDINATE_SETS] = {NULL};
int position_offset = 0;
int color_offset = 0;
int normal_offset = 0;
int texcoord_offsets[MAX_TEXTURE_COORDINATE_SETS] = {0};
resolveMeshInputs(inputs,
position_source,
color_source,
normal_source,
texcoord_sources,
position_offset,
color_offset,
normal_offset,
texcoord_offsets);
unsigned stride = 0;
for (size_t i = 0; i < inputs.getCount(); ++i)
{
stride = osg::maximum<unsigned>(stride, inputs[i]->getOffset());
}
++stride;
VertexIndicesIndexMap vertexIndicesIndexMap;
for (size_t j = 0; j < domPArray.getCount(); ++j)
{
const domListOfUInts& p = domPArray[j]->getValue();
for (size_t i = 0; i < p.getCount(); i += stride)
{
int texcoord_indices[MAX_TEXTURE_COORDINATE_SETS];
for (unsigned int t = 0; t < MAX_TEXTURE_COORDINATE_SETS; ++t)
{
texcoord_indices[t] = p.get(i + texcoord_offsets[t]);
}
VertexIndices v(
p.get(i + position_offset),
p.get(i + color_offset),
p.get(i + normal_offset),
texcoord_indices);
vertexIndicesIndexMap.insert(VertexIndicesIndexMap::value_type(v,
0));
}
}
{
VertexIndicesIndexMap::iterator it = vertexIndicesIndexMap.begin(), end
= vertexIndicesIndexMap.end();
for (GLuint i = 0; it != end; ++it, ++i)
{
it->second = i;
}
}
vertexLists.resize(domPArray.getCount());
for (size_t j = 0; j < domPArray.getCount(); ++j)
{
const domListOfUInts& p = domPArray[j]->getValue();
for (size_t i = 0; i < p.getCount(); i += stride)
{
int texcoord_indices[MAX_TEXTURE_COORDINATE_SETS];
for (unsigned int t = 0; t < MAX_TEXTURE_COORDINATE_SETS; ++t)
{
texcoord_indices[t] = p.get(i + texcoord_offsets[t]);
}
VertexIndices v(
p.get(i + position_offset),
p.get(i + color_offset),
p.get(i + normal_offset),
texcoord_indices);
GLuint index = vertexIndicesIndexMap.find(v)->second;
_oldToNewIndexMap.insert(OldToNewIndexMap::value_type(
OldToNewIndexMap::key_type(pDomMesh, v.position_index),
OldToNewIndexMap::mapped_type(geometry, index)));
vertexLists[j].push_back(index);
}
}
const bool readDoubleVertices = (_precisionHint &
osgDB::Options::DOUBLE_PRECISION_VERTEX) != 0;
const bool readDoubleColors = (_precisionHint &
osgDB::Options::DOUBLE_PRECISION_COLOR) != 0;
const bool readDoubleNormals = (_precisionHint &
osgDB::Options::DOUBLE_PRECISION_NORMAL) != 0;
const bool readDoubleTexcoords = (_precisionHint &
osgDB::Options::DOUBLE_PRECISION_TEX_COORD) != 0;
// Vertices
{
osg::Geometry::ArrayData arrayData( createGeometryData<osg::Vec3Array,
osg::Vec3dArray, VertexIndices::POSITION>(sources[position_source],
vertexIndicesIndexMap, readDoubleVertices) );
if (arrayData.array.valid())
{
geometry->setVertexData(arrayData);
}
}
if (color_source)
{
osg::Geometry::ArrayData arrayData( createGeometryData<osg::Vec4Array,
osg::Vec4dArray, VertexIndices::COLOR>(sources[color_source],
vertexIndicesIndexMap, readDoubleColors) );
if (arrayData.array.valid())
{
geometry->setColorData(arrayData);
}
}
if (normal_source)
{
osg::Geometry::ArrayData arrayData( createGeometryData<osg::Vec3Array,
osg::Vec3dArray, VertexIndices::NORMAL>(sources[normal_source],
vertexIndicesIndexMap, readDoubleNormals) );
if (arrayData.array.valid())
{
geometry->setNormalData(arrayData);
}
}
for (unsigned int texcoord_set = 0; texcoord_set <
MAX_TEXTURE_COORDINATE_SETS; ++texcoord_set)
{
if (daeElement* texcoord_source = texcoord_sources[texcoord_set])
{
// 2D Texcoords
osg::Geometry::ArrayData arrayData(
createGeometryData<osg::Vec2Array, osg::Vec2dArray,
VertexIndices::TEXCOORD>(sources[texcoord_source], vertexIndicesIndexMap,
readDoubleTexcoords, texcoord_set) );
if (arrayData.array.valid())
{
geometry->setTexCoordData(texcoord_set, arrayData);
}
else
{
// 3D Textcoords
osg::Geometry::ArrayData arrayData(
createGeometryData<osg::Vec3Array, osg::Vec3dArray,
VertexIndices::TEXCOORD>(sources[texcoord_source], vertexIndicesIndexMap,
readDoubleTexcoords, texcoord_set) );
if (arrayData.array.valid())
{
geometry->setTexCoordData(texcoord_set, arrayData);
}
}
}
}
#undef FOREACH_INDEX
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This application is open source and may be redistributed and/or modified
* freely and without restriction, both in commercial and non commercial
* applications, as long as this copyright notice is maintained.
*
* This application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <sstream>
#include <memory>
#include <osg/Notify>
#include <osg/NodeVisitor>
#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>
#include <osgDB/ConvertUTF>
#include <OpenThreads/ScopedLock>
#include "ReaderWriterDAE.h"
#include "daeReader.h"
#include "daeWriter.h"
#ifdef WIN32
#include "windows.h"
#endif
#define SERIALIZER() OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
lock(_serializerMutex)
osgDB::ReaderWriter::ReadResult
ReaderWriterDAE::readNode(const std::string& fname,
const osgDB::ReaderWriter::Options* options) const
{
SERIALIZER();
bool bOwnDAE = false;
DAE* pDAE = NULL;
if ( options )
pDAE = (DAE*)options->getPluginData("DAE");
std::string ext( osgDB::getLowerCaseFileExtension(fname) );
if( ! acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED;
std::string fileName( osgDB::findDataFile( fname, options ) );
if( fileName.empty() ) return ReadResult::FILE_NOT_FOUND;
OSG_INFO << "ReaderWriterDAE( \"" << fileName << "\" )" << std::endl;
if (NULL == pDAE)
{
bOwnDAE = true;
pDAE = new DAE;
}
osgDAE::daeReader daeReader(pDAE,
options &&
options->getOptionString().find("StrictTransparency") != std::string::npos,
options ? options->getPrecisionHint() : 0 ) ;
// Convert file name to URI
std::string fileURI = ConvertFilePathToColladaCompatibleURI(fileName);
if ( ! daeReader.convert( fileURI ) )
{
OSG_WARN << "Load failed in COLLADA DOM conversion" << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
if ( options )
{
// Sukender: This is awfully ulgy/dangerous: we're writing in a const
object!
// Return the document URI
if (options->getPluginData("DAE-DocumentURI"))
*(std::string*)options->getPluginData("DAE-DocumentURI") = fileURI;
// Return some additional information about the document
if (options->getPluginData("DAE-AssetUnitName"))
*(std::string*)options->getPluginData("DAE-AssetUnitName") =
daeReader.getAssetUnitName();
if (options->getPluginData("DAE-AssetUnitMeter"))
*(float*)options->getPluginData("DAE-AssetUnitMeter") =
daeReader.getAssetUnitMeter();
if (options->getPluginData("DAE-AssetUp_axis"))
*(domUpAxisType*)options->getPluginData("DAE-AssetUp_axis") =
daeReader.getAssetUpAxis();
}
if (bOwnDAE)
delete pDAE;
osg::Node* rootNode( daeReader.getRootNode() );
return rootNode;
}
///////////////////////////////////////////////////////////////////////////
osgDB::ReaderWriter::WriteResult
ReaderWriterDAE::writeNode( const osg::Node& node,
const std::string& fname, const osgDB::ReaderWriter::Options* options )
const
{
SERIALIZER();
bool bOwnDAE = false;
DAE* pDAE = NULL;
std::string ext( osgDB::getLowerCaseFileExtension(fname) );
if( ! acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED;
// Process options
bool usePolygon(false); // Use plugin option "polygon" to enable
bool googleMode(false); // Use plugin option "GoogleMode" to enable
bool writeExtras(true); // Use plugin option "NoExtras" to disable
bool earthTex(false); // Use plugin option "daeEarthTex" to enable
bool zUpAxis(false); // Use plugin option "daeZUpAxis" to enable
bool linkOrignialTextures(false);
bool forceTexture(false);
bool namesUseCodepage(false);
std::string srcDirectory( osgDB::getFilePath(node.getName().empty() ? fname
: node.getName()) ); // Base dir when relativising images paths
if( options )
{
pDAE = (DAE*)options->getPluginData("DAE");
const std::string & baseDir =
options->getPluginStringData("baseImageDir"); // Rename "srcModelPath"
(and call getFilePath() on it)?
if (!baseDir.empty()) srcDirectory = baseDir;
// Sukender's note: I don't know why DAE seems to accept comma-sparated
options instead of space-separated options as other ReaderWriters. However, to
avoid breaking compatibility, here's a workaround:
std::string optString( options->getOptionString() );
for(std::string::iterator it=optString.begin(); it!=optString.end();
++it) {
if (*it == ' ') *it = ',';
}
std::istringstream iss( optString );
std::string opt;
//while (iss >> opt)
while( std::getline( iss, opt, ',' ) )
{
if( opt == "polygon") usePolygon = true;
else if (opt == "GoogleMode") googleMode = true;
else if (opt == "NoExtras") writeExtras = false;
else if (opt == "daeEarthTex") earthTex = true;
else if (opt == "daeZUpAxis") zUpAxis = true;
else if (opt == "daeLinkOriginalTexturesNoForce") {
linkOrignialTextures = true; forceTexture = false; }
else if (opt == "daeLinkOriginalTexturesForce") {
linkOrignialTextures = true; forceTexture = true; }
else if (opt == "daeNamesUseCodepage") namesUseCodepage = true;
else if (!opt.empty())
{
OSG_NOTICE << std::endl << "COLLADA dae plugin: unrecognized
option \"" << opt << std::endl;
}
}
}
if (NULL == pDAE)
{
bOwnDAE = true;
pDAE = new DAE;
}
// Convert file name to URI
std::string fileURI = ConvertFilePathToColladaCompatibleURI(fname);
osg::NodeVisitor::TraversalMode traversalMode = writeExtras ?
osg::NodeVisitor::TRAVERSE_ALL_CHILDREN :
osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN;
osgDAE::daeWriter daeWriter(pDAE, fileURI, osgDB::getFilePath(fname),
srcDirectory, options, usePolygon, googleMode, traversalMode, writeExtras,
earthTex, zUpAxis, linkOrignialTextures, forceTexture, namesUseCodepage);
daeWriter.setRootNode( node );
const_cast<osg::Node*>(&node)->accept( daeWriter );
osgDB::ReaderWriter::WriteResult retVal( WriteResult::ERROR_IN_WRITING_FILE
);
if ( daeWriter.isSuccess() )
{
if (pDAE->write(fileURI))
retVal = WriteResult::FILE_SAVED;
}
if ( options )
{
if (!bOwnDAE)
{
// Return the document URI used so that users of an external DAE
object
// can locate the correct database
if (options->getPluginData("DAE-DocumentURI"))
*(std::string*)options->getPluginData("DAE-DocumentURI") =
fileURI;
}
}
if (bOwnDAE)
delete pDAE;
return retVal;
}
static void replace(std::string & str, const char from, const std::string & to)
{
// Replace for all occurences
for(std::string::size_type pos=str.find(from); pos!=std::string::npos;
pos=str.find(from))
{
str.replace(pos, 1, to);
}
}
std::string ReaderWriterDAE::ConvertFilePathToColladaCompatibleURI(const
std::string& FilePath)
{
#ifdef OSG_USE_UTF8_FILENAME
std::string path( cdom::nativePathToUri( FilePath ) );
#else
std::string path( cdom::nativePathToUri(
osgDB::convertStringFromCurrentCodePageToUTF8(FilePath) ) );
#endif
// Unfortunately, cdom::nativePathToUri() does not convert '#' characters
to "%23" as expected.
// So having /a/#b/c will generate a wrong conversion, as '#' will be
misinterpreted as an URI fragment.
// Here are listed all special chars, but only # was found problematic. I
(Sukender) tested #{}^~[]`;@=&$ under Windows.
// Uncomment lines if you find issues with some other special characters.
//replace(path, '%', "%25"); // % at first
//replace(path, ' ', "%20");
replace(path, '#', "%23");
//replace(path, '<', "%3C");
//replace(path, '>', "%3E");
//replace(path, '{', "%7B");
//replace(path, '}', "%7D");
//replace(path, '|', "%7C");
//replace(path, '^', "%5E");
//replace(path, '~', "%7E");
//replace(path, '[', "%5B");
//replace(path, '}', "%5D");
//replace(path, '`', "%60");
//replace(path, ';', "%3B");
//replace(path, '?', "%3F");
//replace(path, '@', "%40");
//replace(path, '=', "%3D");
//replace(path, '&', "%26");
//replace(path, '$', "%24");
return path;
}
///////////////////////////////////////////////////////////////////////////
// Add ourself to the Registry to instantiate the reader/writer.
REGISTER_OSGPLUGIN(dae, ReaderWriterDAE)
// vim: set sw=4 ts=8 et ic ai:
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org