1 /*
2     Copyright (c) 2008-2009 NetAllied Systems GmbH
3 
4     This file is part of COLLADASaxFrameworkLoader.
5 
6     Licensed under the MIT Open Source License,
7     for details please see LICENSE file or the website
8     http://www.opensource.org/licenses/mit-license.php
9 */
10 
11 #include "COLLADASaxFWLStableHeaders.h"
12 #include "COLLADASaxFWLDocumentProcessor.h"
13 
14 #include "COLLADAFWNode.h"
15 #include "COLLADAFWIWriter.h"
16 
17 
18 namespace COLLADASaxFWL
19 {
20 
21 	//-----------------------------
DocumentProcessor(Loader * colladaLoader,SaxParserErrorHandler * saxParserErrorHandler,int objectFlags,int & parsedObjectFlags)22 	DocumentProcessor::DocumentProcessor ( Loader* colladaLoader,
23 		SaxParserErrorHandler* saxParserErrorHandler,
24 		int objectFlags,
25 		int& parsedObjectFlags)
26 		: mColladaLoader( colladaLoader )
27 		, mCurrentSidTreeNode( colladaLoader->getSidTreeRoot() )
28 		, mIdStringSidTreeNodeMap( colladaLoader->getIdStringSidTreeNodeMap() )
29 		, mVisualScenes( colladaLoader->getVisualScenes() )
30 		, mLibraryNodes( colladaLoader->getLibraryNodes() )
31 		, mEffects( colladaLoader->getEffects() )
32 		, mLights( colladaLoader->getLights() )
33 		, mCameras( colladaLoader->getCameras() )
34 		, mKinematicsIntermediateData( colladaLoader->getKinematicsIntermediateData() )
35 		, mFormulasMap( colladaLoader->getFormulasMap() )
36 		, mAnimationSidAddressBindings( colladaLoader->getAnimationSidAddressBindings() )
37 		, mUniqueIdAnimationListMap( colladaLoader->getUniqueIdAnimationListMap() )
38 		, mObjectFlags( objectFlags )
39 		, mParsedObjectFlags( parsedObjectFlags )
40 		, mSkinDataJointSidsMap( colladaLoader->getSkinDataJointSidsMap() )
41 		, mInstanceControllerDataListMap( colladaLoader->getInstanceControllerDataListMap() )
42 		, mSkinDataSkinSourceMap( colladaLoader->getSkinDataSkinSourceMap() )
43 		, mSkinControllerSet( colladaLoader->getSkinControllerSet() )
44 		, mSaxParserErrorHandler( saxParserErrorHandler )
45 	{
46 
47 	}
48     //------------------------------
~DocumentProcessor()49 	DocumentProcessor::~DocumentProcessor()
50 	{
51 	}
52 
53 	//---------------------------------
addToSidTree(const char * colladaId,const char * colladaSid)54 	SidTreeNode* DocumentProcessor::addToSidTree( const char* colladaId, const char* colladaSid )
55 	{
56 		mCurrentSidTreeNode = mCurrentSidTreeNode->createAndAddChild( colladaSid ? colladaSid : "");
57 
58 		if ( colladaId && *colladaId )
59 		{
60 			mIdStringSidTreeNodeMap[colladaId] = mCurrentSidTreeNode;
61 		}
62 		return mCurrentSidTreeNode;
63 	}
64 
65 	//---------------------------------
moveUpInSidTree()66 	void DocumentProcessor::moveUpInSidTree()
67 	{
68 		COLLADABU_ASSERT(mCurrentSidTreeNode);
69 		mCurrentSidTreeNode = mCurrentSidTreeNode->getParent();
70 	}
71 
72 	//---------------------------------
resolveId(const String & id)73 	const SidTreeNode* DocumentProcessor::resolveId( const String& id )
74 	{
75 		return findSidTreeNodeByStringId( id );
76 	}
77 
78 	//---------------------------------
resolveSid(const SidAddress & sidAddress)79 	const SidTreeNode* DocumentProcessor::resolveSid( const SidAddress& sidAddress )
80 	{
81 		if ( !sidAddress.isValid() )
82 			return 0;
83 
84 		SidTreeNode* startingPoint = 0;
85 		const String& id = sidAddress.getId();
86 		if ( !id.empty() )
87 		{
88 			// search for element with id
89 			startingPoint = findSidTreeNodeByStringId( id );
90 		}
91 
92 		if ( !startingPoint )
93 			return 0;
94 
95 		SidTreeNode* currentNode = startingPoint;
96 		const SidAddress::SidList& sids = sidAddress.getSids();
97 
98 		size_t i = 0;
99 
100 		if ( !sids.empty() && (sids.front() == startingPoint->getSid()) )
101 		{
102 			// the first one is the start element it self exclude it from recursive search
103 			i = 1;
104 		}
105 
106 		for ( size_t count = sids.size(); i < count; ++i)
107 		{
108 			const String& currentSid = sids[i];
109 			SidTreeNode* childNode = currentNode->findChildBySid( currentSid );
110 
111 			if ( !childNode )
112 			{
113 				// we could not find the sid as a child of currentNode
114 				// lets try if the sid is in an instantiated element
115 				return resolveSidInInstance( currentNode, sidAddress, i);
116 			}
117 			else
118 			{
119 				currentNode = childNode;
120 			}
121 		}
122 		return currentNode;
123 	}
124 
125 	//---------------------------------
resolveSid(const COLLADABU::URI & id,const String & sid)126 	const SidTreeNode* DocumentProcessor::resolveSid( const COLLADABU::URI& id,  const String& sid)
127 	{
128 		SidAddress sidAddress(id, sid);
129 		return resolveSid(sidAddress);
130 	}
131 
132 	//---------------------------------
resolveSidInInstance(const SidTreeNode * instancingElement,const SidAddress & sidAddress,size_t firstSidIndex)133 	const SidTreeNode* DocumentProcessor::resolveSidInInstance( const SidTreeNode* instancingElement, const SidAddress& sidAddress,  size_t firstSidIndex)
134 	{
135 		// the sid address we use to resolve the sid in the instantiated element
136 		const COLLADABU::URI* uri = 0;
137 
138 		//check if instancingElement instantiates an element
139 		switch ( instancingElement->getTargetType() )
140 		{
141 		case SidTreeNode::TARGETTYPECLASS_INTERMEDIATETARGETABLE:
142 			{
143 				IntermediateTargetable* iTargetable = instancingElement->getIntermediateTargetableTarget();
144 				switch ( iTargetable->getClassId() )
145 				{
146 				case INTERMEDIATETARGETABLE_TYPE::KINEMATICS_INSTANCE:
147 					{
148 						KinematicInstance* ki = intermediateTargetableSafeCast<KinematicInstance>(iTargetable);
149 						uri = &ki->getUrl();
150 						break;
151 					}
152 				}
153 
154 			}
155 			// 		case SidTreeNode::TARGETTYPECLASS_OBJECT:
156 			// 			{
157 			// 				COLLADAFW::Object* iObject = instancingElement->getObjectTarget();
158 			// 				switch ( iObject->getClassId() )
159 			// 				{
160 			// 				case COLLADAFW::COLLADA_TYPE::JOINT:
161 			// 					{
162 			// 						KinematicInstance* ki = (KinematicInstance*) iObject;
163 			// 						uri = &ki->getUrl();
164 			// 					}
165 			// 				}
166 			// 			}
167         default:
168             break;
169 		}
170 
171 		if ( !uri )
172 		{
173 			// we could not find an instantiated element
174 			return 0;
175 		}
176 
177 		SidAddress newSidAddress( *uri );
178 		const SidAddress::SidList& allSids = sidAddress.getSids();
179 		size_t allSidsCount = allSids.size();
180 		for ( size_t i = firstSidIndex; i < allSidsCount; ++i)
181 		{
182 			newSidAddress.appendSid( allSids[i] );
183 		}
184 		newSidAddress.setFirstIndex( sidAddress.getFirstIndex() );
185 		newSidAddress.setSecondIndex( sidAddress.getSecondIndex() );
186 		newSidAddress.setMemberSelection( sidAddress.getMemberSelection() );
187 		newSidAddress.setMemberSelectionName( sidAddress.getMemberSelectionName() );
188 		return resolveSid( newSidAddress );
189 	}
190 
191 
192 	//-----------------------------
findSidTreeNodeByStringId(const String & id)193 	SidTreeNode* DocumentProcessor::findSidTreeNodeByStringId( const String& id )
194 	{
195 		Loader::IdStringSidTreeNodeMap::iterator it = mIdStringSidTreeNodeMap.find(id);
196 		if ( it == mIdStringSidTreeNodeMap.end() )
197 		{
198 			return 0;
199 		}
200 		else
201 		{
202 			return it->second;
203 		}
204 	}
205 
206 	//-----------------------------
addToAnimationSidAddressBindings(const AnimationInfo & animationInfo,const SidAddress & targetSidAddress)207 	void DocumentProcessor::addToAnimationSidAddressBindings( const AnimationInfo& animationInfo, const SidAddress& targetSidAddress )
208 	{
209 		Loader::AnimationSidAddressBinding binding( animationInfo, targetSidAddress);
210 		mAnimationSidAddressBindings.push_back(binding);
211 	}
212 
getAnimationListByUniqueId(const COLLADAFW::UniqueId & animationListUniqueId)213 	COLLADAFW::AnimationList*& DocumentProcessor::getAnimationListByUniqueId( const COLLADAFW::UniqueId& animationListUniqueId )
214 	{
215 		return mUniqueIdAnimationListMap[animationListUniqueId];
216 	}
217 
218 	//-----------------------------
addSkinDataJointSidsPair(const COLLADAFW::UniqueId & skinDataUniqueId,const StringList & sidsOrIds,bool areIds)219 	void DocumentProcessor::addSkinDataJointSidsPair( const COLLADAFW::UniqueId& skinDataUniqueId, const StringList& sidsOrIds, bool areIds )
220 	{
221 		Loader::JointSidsOrIds jointSidsOrIds;
222 		jointSidsOrIds.sidsOrIds = sidsOrIds;
223 		jointSidsOrIds.areIds = areIds;
224 		mSkinDataJointSidsMap[skinDataUniqueId]=jointSidsOrIds;
225 	}
226 
227 	//-----------------------------
getJointSidsOrIdsBySkinDataUniqueId(const COLLADAFW::UniqueId & skinDataUniqueId) const228 	const Loader::JointSidsOrIds& DocumentProcessor::getJointSidsOrIdsBySkinDataUniqueId( const COLLADAFW::UniqueId& skinDataUniqueId ) const
229 	{
230 		Loader::SkinDataJointSidsMap::const_iterator it = mSkinDataJointSidsMap.find(skinDataUniqueId);
231 		if ( it != mSkinDataJointSidsMap.end() )
232 		{
233 			return it->second;
234 		}
235 		else
236 		{
237 			return Loader::EMPTY_JOINTSIDSORIDS;
238 		}
239 	}
240 
241 	//-----------------------------
addSkinDataSkinSourcePair(const COLLADAFW::UniqueId & skinDataUniqueId,const COLLADABU::URI & skinSource)242 	void DocumentProcessor::addSkinDataSkinSourcePair( const COLLADAFW::UniqueId& skinDataUniqueId, const COLLADABU::URI& skinSource )
243 	{
244 		mSkinDataSkinSourceMap[skinDataUniqueId]=skinSource;
245 	}
246 
247 	//-----------------------------
getSkinSourceBySkinDataUniqueId(const COLLADAFW::UniqueId & skinDataUniqueId) const248 	const COLLADABU::URI* DocumentProcessor::getSkinSourceBySkinDataUniqueId( const COLLADAFW::UniqueId& skinDataUniqueId ) const
249 	{
250 		Loader::SkinDataSkinSourceMap::const_iterator it = mSkinDataSkinSourceMap.find(skinDataUniqueId);
251 		if ( it != mSkinDataSkinSourceMap.end() )
252 		{
253 			return &it->second;
254 		}
255 		else
256 		{
257 			return 0;
258 		}
259 	}
260 
261 	//-----------------------------
getInstanceControllerDataListByControllerUniqueId(const COLLADAFW::UniqueId & controllerUniqueId) const262 	const Loader::InstanceControllerDataList& DocumentProcessor::getInstanceControllerDataListByControllerUniqueId( const COLLADAFW::UniqueId& controllerUniqueId ) const
263 	{
264 		Loader::InstanceControllerDataListMap::const_iterator it = mInstanceControllerDataListMap.find(controllerUniqueId);
265 		if ( it != mInstanceControllerDataListMap.end())
266 		{
267 			return it->second;
268 		}
269 		else
270 		{
271 			return Loader::EMPTY_INSTANCE_CONTROLLER_DATALIST;
272 		}
273 
274 	}
275 
276 	//-----------------------------
getInstanceControllerDataListByControllerUniqueId(const COLLADAFW::UniqueId & controllerUniqueId)277 	Loader::InstanceControllerDataList& DocumentProcessor::getInstanceControllerDataListByControllerUniqueId( const COLLADAFW::UniqueId& controllerUniqueId )
278 	{
279 		return mInstanceControllerDataListMap[controllerUniqueId];
280 	}
281 
282 
283 
284 	//-----------------------------
setCOLLADAVersion(COLLADAVersion cOLLADAVersion)285 	void DocumentProcessor::setCOLLADAVersion( COLLADAVersion cOLLADAVersion )
286 	{
287 		mCOLLADAVersion = cOLLADAVersion;
288 		mColladaLoader->setCOLLADAVersion(cOLLADAVersion);
289 	}
290 
291 	//-----------------------------
createAndWriteSkinController(const Loader::InstanceControllerData & instanceControllerData,const COLLADAFW::UniqueId & controllerDataUniqueId,const COLLADAFW::UniqueId & sourceUniqueId)292 	bool DocumentProcessor::createAndWriteSkinController( const Loader::InstanceControllerData& instanceControllerData,
293 		const COLLADAFW::UniqueId& controllerDataUniqueId,
294 		const COLLADAFW::UniqueId& sourceUniqueId)
295 	{
296 		if ( !controllerDataUniqueId.isValid() )
297 			return false;
298 		const Loader::JointSidsOrIds& sidsOrIds = getJointSidsOrIdsBySkinDataUniqueId( controllerDataUniqueId );
299 		return createAndWriteSkinController( instanceControllerData, controllerDataUniqueId, sourceUniqueId, sidsOrIds.sidsOrIds, sidsOrIds.areIds );
300 	}
301 
302 	//-----------------------------
createAndWriteSkinController(const Loader::InstanceControllerData & instanceControllerData,const COLLADAFW::UniqueId & controllerDataUniqueId,const COLLADAFW::UniqueId & sourceUniqueId,const StringList & sidsOrIds,bool resolveIds)303 	bool DocumentProcessor::createAndWriteSkinController( const Loader::InstanceControllerData& instanceControllerData,
304 		const COLLADAFW::UniqueId& controllerDataUniqueId,
305 		const COLLADAFW::UniqueId& sourceUniqueId,
306 		const StringList& sidsOrIds,
307 		bool resolveIds)
308 	{
309 		if ( !controllerDataUniqueId.isValid() )
310 			return false;
311 
312 		const URIList& skeletonRoots = instanceControllerData.skeletonRoots;
313 
314 		NodeList joints;
315 
316 		for ( StringList::const_iterator it = sidsOrIds.begin(); it != sidsOrIds.end(); ++it)
317 		{
318 			const String sidOrId = *it;
319 
320 			bool jointFound = false;
321 			if ( resolveIds )
322 			{
323 				// Joints are referenced by Id
324 				const SidTreeNode* joint = resolveId( sidOrId );
325 				if ( joint )
326 				{
327 					jointFound = addValidatedJoint(*joint, joints);
328 				}
329 			}
330 			else if ( skeletonRoots.size() == 0 )
331 			{
332 				// Joints are referenced by Sid and no <skeleton> entries are defined
333 				const SidTreeNode* joint = resolveSid( sidOrId );
334 				if ( joint )
335 				{
336 					jointFound = addValidatedJoint(*joint, joints);
337 				}
338 			}
339 			else
340 			{
341 				// we get the list from the <skeleton> entries
342 				for ( URIList::const_iterator skeletonIt = skeletonRoots.begin(); skeletonIt != skeletonRoots.end(); ++skeletonIt)
343 				{
344 					const COLLADABU::URI& skeletonUri = *skeletonIt;
345 
346 					SidAddress sidAddress( skeletonUri, sidOrId );
347 					const SidTreeNode* joint = resolveSid( sidAddress );
348 					if ( joint )
349 					{
350                         jointFound = addValidatedJoint(*joint, joints);
351 						if( jointFound )
352 						{
353 							//search for the next joint
354 							break;
355 						}
356 					}
357 				}
358 			}
359 
360 
361 			if ( !jointFound )
362 			{
363 				std::stringstream msg;
364 				msg << "Could not resolve " << (resolveIds ? "id" : "sid") << " \"";
365 				msg << sidOrId << "\" referenced in skin controller.";
366 				if ( handleFWLError( SaxFWLError::ERROR_UNRESOLVED_REFERENCE, msg.str() ))
367 				{
368 					return false;
369 				}
370 			}
371 		}
372 
373 		COLLADAFW::SkinController skinController( createUniqueId(COLLADAFW::SkinController::ID()));
374 
375 		COLLADAFW::UniqueIdArray &jointsUniqueIds = skinController.getJoints();
376 		jointsUniqueIds.allocMemory( joints.size() );
377 		jointsUniqueIds.setCount(joints.size());
378 
379 		size_t i = 0;
380 		NodeList::const_iterator it = joints.begin();
381 		for ( ; it != joints.end(); ++it, ++i )
382 		{
383 			const COLLADAFW::Node* node = *it;
384 			jointsUniqueIds[i] = node->getUniqueId();
385 		}
386 
387 		skinController.setSkinControllerData(controllerDataUniqueId);
388 		skinController.setSource(sourceUniqueId);
389 
390 		bool success = true;
391 		// Check if we have already wrote a skin controller that describes the same controller, i.e. has same
392 		// source, skin data and joints. If so, do not write it again and reference the previously used in the
393 		// scene graph
394 		const COLLADAFW::SkinController* skinControllerToWrite = 0;
395 		Loader::SkinControllerSet::const_iterator skinControllerIt = mSkinControllerSet.find( skinController );
396 		if ( skinControllerIt == mSkinControllerSet.end() )
397 		{
398 			skinControllerToWrite = &skinController;
399 			success = writer()->writeController(skinControllerToWrite);
400 			mSkinControllerSet.insert( skinController );
401 		}
402 		else
403 		{
404 			skinControllerToWrite = &(*skinControllerIt);
405 		}
406 
407 		instanceControllerData.instanceController->setInstanciatedObjectId( skinControllerToWrite->getUniqueId() );
408 
409 		return success;
410 	}
411 
412 
413 	//-----------------------------
createAndWriteSkinControllers()414 	bool DocumentProcessor::createAndWriteSkinControllers()
415 	{
416 		Loader::InstanceControllerDataListMap::const_iterator mapIt = mInstanceControllerDataListMap.begin();
417 
418 		for ( ; mapIt != mInstanceControllerDataListMap.end(); ++mapIt )
419 		{
420 			const COLLADAFW::UniqueId& skinDataUniqueId = mapIt->first;
421 			const Loader::InstanceControllerDataList& instanceControllerDataList = mapIt->second;
422 
423 			Loader::InstanceControllerDataList::const_iterator listIt = instanceControllerDataList.begin();
424 
425 			for ( ; listIt != instanceControllerDataList.end(); ++listIt)
426 			{
427 				const Loader::InstanceControllerData& instanceControllerData = *listIt;
428 				const COLLADABU::URI* sourceUrl = getSkinSourceBySkinDataUniqueId( skinDataUniqueId );
429 
430 				if ( !sourceUrl )
431 				{
432 					// TODO handle error
433 					continue;
434 				}
435 
436 				const COLLADAFW::UniqueId& sourceUniqueId = getUniqueIdByUrl(*sourceUrl, true);
437 				if ( !sourceUniqueId.isValid() )
438 				{
439 					// TODO handle error
440 					continue;
441 				}
442 				if ( !createAndWriteSkinController( instanceControllerData, skinDataUniqueId, sourceUniqueId ) )
443 					return false;
444 			}
445 		}
446 		return true;
447 	}
448 
449 	//------------------------------
addValidatedJoint(const SidTreeNode & joint,NodeList & joints)450 	bool DocumentProcessor::addValidatedJoint(const SidTreeNode &joint, NodeList &joints)
451 	{
452 		bool jointValid = false;
453 
454 		// the joint could be found
455 		if ( joint.getTargetType() == SidTreeNode::TARGETTYPECLASS_OBJECT )
456 		{
457 			const COLLADAFW::Object* object = joint.getObjectTarget();
458 
459 			if ( object->getClassId() == COLLADAFW::Node::ID() )
460 			{
461 				joints.push_back( (COLLADAFW::Node*)object );
462 
463 				jointValid = true;
464 				//search for the next joint
465 			}
466 			else
467 			{
468 				// we could resolve the sid, but is not a joint/node
469 			}
470 		}
471 		else
472 		{
473 			// we could resolve the sid, but is not a joint/node
474 		}
475 		return jointValid;
476 	}
477 
478 	//------------------------------
addKinematicsScene(KinematicsScene * kinematicsScene)479 	void DocumentProcessor::addKinematicsScene( KinematicsScene* kinematicsScene )
480 	{
481 		const COLLADABU::URI& uri = kinematicsScene->getUri();
482 		mKinematicsIntermediateData.getKinematicsScenes().insert(std::make_pair(uri, kinematicsScene));
483 	}
484 
485 	//------------------------------
getKinematicsSceneByUri(const COLLADABU::URI & uri)486 	KinematicsScene* DocumentProcessor::getKinematicsSceneByUri( const COLLADABU::URI& uri )
487 	{
488 		const KinematicsIntermediateData::KinematicsSceneMap& map = mKinematicsIntermediateData.getKinematicsScenes();
489 		KinematicsIntermediateData::KinematicsSceneMap::const_iterator it = map.find(uri);
490 		if ( it != map.end() )
491 		{
492 			return it->second;
493 		}
494 		else
495 		{
496 			return 0;
497 		}
498 	}
499 
500 	//------------------------------
addKinematicsController(KinematicsController * kinematicsController)501 	void DocumentProcessor::addKinematicsController( KinematicsController* kinematicsController )
502 	{
503 		const COLLADABU::URI& uri = kinematicsController->getUri();
504 		mKinematicsIntermediateData.getKinematicsControllers().insert(std::make_pair(uri, kinematicsController));
505 	}
506 
507 	//------------------------------
getKinematicsControllerByUri(const COLLADABU::URI & uri)508 	KinematicsController* DocumentProcessor::getKinematicsControllerByUri( const COLLADABU::URI& uri )
509 	{
510 		const KinematicsIntermediateData::KinematicsControllerMap& map = mKinematicsIntermediateData.getKinematicsControllers();
511 		KinematicsIntermediateData::KinematicsControllerMap::const_iterator it = map.find(uri);
512 		if ( it != map.end() )
513 		{
514 			return it->second;
515 		}
516 		else
517 		{
518 			return 0;
519 		}
520 	}
521 
522 	//------------------------------
addKinematicsModel(KinematicsModel * kinematicsModel)523 	void DocumentProcessor::addKinematicsModel( KinematicsModel* kinematicsModel )
524 	{
525 		const COLLADABU::URI& uri = kinematicsModel->getUrl();
526 		mKinematicsIntermediateData.getKinematicsModels().insert(std::make_pair(uri, kinematicsModel));
527 	}
528 
529 	//------------------------------
getKinematicsModelByUri(const COLLADABU::URI & uri)530 	KinematicsModel* DocumentProcessor::getKinematicsModelByUri( const COLLADABU::URI& uri )
531 	{
532 		const KinematicsIntermediateData::KinematicsModelMap& map = mKinematicsIntermediateData.getKinematicsModels();
533 		KinematicsIntermediateData::KinematicsModelMap::const_iterator it = map.find(uri);
534 		if ( it != map.end() )
535 		{
536 			return it->second;
537 		}
538 		else
539 		{
540 			return 0;
541 		}
542 	}
543 
544 	//------------------------------
addFormula(COLLADAFW::Formula * formula)545 	void DocumentProcessor::addFormula( COLLADAFW::Formula* formula )
546 	{
547 		mFormulasMap.insert(std::make_pair(formula->getUniqueId(), formula));
548 	}
549 
550 	//------------------------------
getFormulaByUniqueId(const COLLADAFW::UniqueId & uniqueId) const551 	COLLADAFW::Formula* DocumentProcessor::getFormulaByUniqueId( const COLLADAFW::UniqueId& uniqueId ) const
552 	{
553 		COLLADAFW::Formula* formula = 0;
554 		Loader::UniqueIdFormulaMap::const_iterator it = mFormulasMap.find(uniqueId);
555 		if ( it != mFormulasMap.end() )
556 		{
557 			formula = it->second;
558 		}
559 		return formula;
560 	}
561 
562 
563 } // namespace COLLADASaxFWL
564