/* * 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 #include #include #include #include #include #include using namespace osgDAE; daeReader::Options::Options() : strictTransparency(false), precisionHint(0), usePredefinedTextureUnits(true), tessellateMode(TESSELLATE_POLYGONS_AS_TRIFAN) // Use old tessellation behaviour as default { } daeReader::daeReader(DAE *dae_, const Options * pluginOptions) : _dae(dae_), _rootNode(NULL), _document(NULL), _visualScene(NULL), _numlights(0), _currentInstance_effect(NULL), _currentEffect(NULL), _authoringTool(UNKNOWN), _invertTransparency(false), _pluginOptions(pluginOptions ? *pluginOptions : Options()), _assetUnitName("meter"), _assetUnitMeter(1.0), _assetUp_axis(UPAXISTYPE_Y_UP) { } daeReader::~daeReader() { } bool daeReader::convert( const std::string &fileURI ) { // Clear caches _geometryMap.clear(); _materialMap.clear(); _materialMap2.clear(); daeElement *colladaElement; daeInt count, result; _document = _dae->open(fileURI); if (!_document) { OSG_WARN << "Load failed in COLLADA DOM" << std::endl; return false; } OSG_INFO << "URI loaded: " << fileURI << std::endl; if ( !_document->getScene() || !_document->getScene()->getInstance_visual_scene() ) { OSG_WARN << "No scene found!" << std::endl; return false; } if (_document->getAsset()) { const domAsset::domContributor_Array& ContributorArray = _document->getAsset()->getContributor_array(); size_t NumberOfContributors = ContributorArray.getCount(); size_t CurrentContributor; for (CurrentContributor = 0; CurrentContributor < NumberOfContributors; CurrentContributor++) { if (ContributorArray[CurrentContributor]->getAuthoring_tool()) { const char szBlender[] = "Blender"; const char szDazStudio[] = "DAZ|Studio"; const char szSketchup[] = "Google SketchUp"; const char szFbx[] = "FBX"; const char szMaya[] = "Maya"; xsString Tool = ContributorArray[CurrentContributor]->getAuthoring_tool()->getValue(); if (strncmp(Tool, szBlender, strlen(szBlender)) == 0) _authoringTool = BLENDER; else if (strncmp(Tool, szDazStudio, strlen(szDazStudio)) == 0) _authoringTool = DAZ_STUDIO; else if (strncmp(Tool, szFbx, strlen(szFbx)) == 0) _authoringTool = FBX_CONVERTER; else if (strncmp(Tool, szSketchup, strlen(szSketchup)) == 0) _authoringTool = GOOGLE_SKETCHUP; else if (strncmp(Tool, szMaya, strlen(szMaya)) == 0) _authoringTool = MAYA; } } if (_document->getAsset()->getUnit()) { if (NULL != _document->getAsset()->getUnit()->getName()) _assetUnitName = std::string(_document->getAsset()->getUnit()->getName()); if (0 != _document->getAsset()->getUnit()->getMeter()) _assetUnitMeter = _document->getAsset()->getUnit()->getMeter(); } if (_document->getAsset()->getUp_axis()) _assetUp_axis = _document->getAsset()->getUp_axis()->getValue(); } domInstanceWithExtra *ivs = _document->getScene()->getInstance_visual_scene(); _visualScene = daeSafeCast< domVisual_scene >( getElementFromURI( ivs->getUrl() ) ); if ( _visualScene == NULL ) { OSG_WARN << "Unable to locate visual scene!" << std::endl; return false; } if (daeDatabase* database = _dae->getDatabase()) { _invertTransparency = findInvertTransparency(database); // build a std::map for lookup if Group or PositionAttitudeTransform should be created, // i.e, make it easy to check if a instance_rigid_body targets a visual node domInstance_rigid_body *pDomInstanceRigidBody; count = database->getElementCount(NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY, NULL); for (int i=0; igetElement(&colladaElement, i, NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY); if (result == DAE_OK) { pDomInstanceRigidBody = daeSafeCast(colladaElement); if (pDomInstanceRigidBody) { domNode *node = daeSafeCast(pDomInstanceRigidBody->getTarget().getElement()); if (node && node->getId()) { _targetMap[ std::string(node->getId()) ] = true; } } } } // Build a map of elements that are targetted by animations count = database->getElementCount(NULL, COLLADA_TYPE_CHANNEL, NULL); for (int i=0; igetElement(&colladaElement, i, NULL, COLLADA_TYPE_CHANNEL); if (result == DAE_OK) { domChannel* pDomChannel = daeSafeCast(colladaElement); if (pDomChannel) { std::string target = pDomChannel->getTarget(); size_t openparenthesis = target.find_first_of('('); if (openparenthesis != std::string::npos) target.erase(openparenthesis); daeSIDResolver resolver(pDomChannel, target.c_str()); daeElement *pDaeElement = resolver.getElement(); if (pDaeElement) { _daeElementDomChannelMap.insert(daeElementDomChannelMap::value_type(pDaeElement, pDomChannel)); } else { OSG_WARN << "Could not locate target " << pDomChannel->getTarget()<< std::endl; } } } } // Find all nodes that are used as bones. Note that while many files // identify nodes with type="JOINT", some don't do this, while others // identify every node as a joint, making it meaningless. std::vector instanceControllers; database->typeLookup(instanceControllers); for (size_t i = 0; i < instanceControllers.size(); ++i) { domInstance_controller* pInstanceController = instanceControllers[i]; domController *pDomController = daeSafeCast(getElementFromURI(pInstanceController->getUrl())); if (!pDomController) { OSG_WARN << "Failed to locate controller " << pInstanceController->getUrl().getURI() << std::endl; continue; } const domInstance_controller::domSkeleton_Array& domSkeletonURIs = pInstanceController->getSkeleton_array(); std::vector searchIn; for (size_t i = 0; i < domSkeletonURIs.getCount(); ++i) { if (daeElement* el = getElementFromURI(domSkeletonURIs[i]->getValue())) { searchIn.push_back(el); if (domNode* pJoint = daeSafeCast(el)) { _jointSet.insert(pJoint); } } } if (searchIn.empty()) { searchIn.push_back(_visualScene); } const domSkin* pSkin = pDomController->getSkin(); if (!pSkin) continue; const domSkin::domJoints* pJoints = pSkin->getJoints(); if (!pJoints) continue; const domInputLocal_Array& inputURIs = pJoints->getInput_array(); domSource* pDomJointsSource = NULL; for (size_t i=0; i < inputURIs.getCount(); i++) { if (!strcmp(inputURIs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT)) { pDomJointsSource = daeSafeCast(getElementFromURI(inputURIs[i]->getSource())); if (!pDomJointsSource) { OSG_WARN << "Could not find skin joints source '" << inputURIs[i]->getSource().getURI() << "'" <getIDREF_array()) { for (size_t i = 0; i < pDomIDREFs->getCount(); ++i) { if (domNode* pJoint = daeSafeCast(getElementFromIDRef(pDomIDREFs->getValue().get(i)))) { _jointSet.insert(pJoint); } } } else if (domName_array* pDomNames = pDomJointsSource->getName_array()) { for (size_t i = 0; i < pDomNames->getCount(); ++i) { daeString target = pDomNames->getValue().get(i); for (size_t j = 0; j < searchIn.size(); ++j) { daeSIDResolver resolver(searchIn[j], target); if (domNode* pJoint = daeSafeCast(resolver.getElement())) { _jointSet.insert(pJoint); } } } } } } // Build the actual scene graph based on the visual scene _rootNode = processVisualScene( _visualScene ); osgAnimation::BasicAnimationManager* pOsgAnimationManager = processAnimationLibraries(_document); if (pOsgAnimationManager) { _rootNode->addUpdateCallback(pOsgAnimationManager); } return true; } void daeReader::addChild(osg::Group* group, osg::Node* node) { if (dynamic_cast(node)) { unsigned index = 0; while (index < group->getNumChildren() && dynamic_cast(group->getChild(index))) { ++index; } group->insertChild(index, node); } else { group->addChild(node); } } osg::Group* daeReader::turnZUp() { osg::PositionAttitudeTransform* pat = NULL; // If not Z axis up we need to rotate scene to bring the Z axis up if (_assetUp_axis != UPAXISTYPE_Z_UP) { pat = new osg::PositionAttitudeTransform(); if (_assetUp_axis == UPAXISTYPE_Y_UP) { pat->setAttitude(osg::Quat(osg::inDegrees(90.0f), osg::Vec3(1.0f,0.0f,0.0f))); } else //(m_AssetUp_axis == UPAXISTYPE_X_UP) { pat->setAttitude(osg::Quat(osg::inDegrees(90.0f), osg::Vec3(0.0f,1.0f,0.0f))); } } _assetUp_axis = UPAXISTYPE_Z_UP; return pat; } osg::Group* daeReader::processVisualScene( domVisual_scene *scene ) { osg::Group *retVal; _rootStateSet = new osg::StateSet(); unsigned int nbVisualSceneGroup=scene->getNode_array().getCount(); if (nbVisualSceneGroup==0) { OSG_WARN << "No visual scene group found !" << std::endl; retVal = new osg::Group(); retVal->setName("Empty Collada scene"); } else { retVal = turnZUp(); if (!retVal) { retVal = new osg::Group; } _skinInstanceControllers.clear(); const domNode_Array& node_array = scene->getNode_array(); for (size_t i = 0; i < node_array.getCount(); i++) { if (osg::Node* node = processNode(node_array[i], false)) { addChild(retVal, node); } } processSkins(); if (retVal->getName().empty()) { if (retVal->getNumChildren()) { retVal->setName("Collada visual scene group"); } else { retVal->setName("Empty Collada scene (import failure)"); } } } retVal->setStateSet(_rootStateSet.get()); return retVal; } osg::Group* daeReader::processExtras(domNode *node) { // See if one of the extras contains OpenSceneGraph specific information unsigned int numExtras = node->getExtra_array().getCount(); for (unsigned int currExtra=0; currExtra < numExtras; currExtra++) { domExtra* extra = node->getExtra_array()[currExtra]; domTechnique* teq = NULL; daeString extraType = extra->getType(); if (extraType) { if (strcmp(extraType, "Switch") == 0) { teq = getOpenSceneGraphProfile(extra); if (teq) { return processOsgSwitch(teq); } } else if (strcmp(extraType, "MultiSwitch") == 0) { teq = getOpenSceneGraphProfile(extra); if (teq) { return processOsgMultiSwitch(teq); } } else if (strcmp(extraType, "LOD") == 0) { teq = getOpenSceneGraphProfile(extra); if (teq) { return processOsgLOD(teq); } } else if (strcmp(extraType, "DOFTransform") == 0) { teq = getOpenSceneGraphProfile(extra); if (teq) { return processOsgDOFTransform(teq); } } else if (strcmp(extraType, "Sequence") == 0) { teq = getOpenSceneGraphProfile(extra); if (teq) { return processOsgSequence(teq); } } } } return new osg::Group; } void daeReader::processNodeExtra(osg::Node* osgNode, domNode *node) { // See if one of the extras contains OpenSceneGraph specific information unsigned int numExtras = node->getExtra_array().getCount(); for (unsigned int currExtra=0; currExtra < numExtras; currExtra++) { domExtra* extra = node->getExtra_array()[currExtra]; daeString extraType = extra->getType(); if (extraType && (strcmp(extraType, "Node") == 0)) { domTechnique* teq = getOpenSceneGraphProfile(extra); if (teq) { domAny* any = daeSafeCast< domAny >(teq->getChild("Descriptions")); if (any) { osg::Node::DescriptionList descriptions; unsigned int numChildren = any->getChildren().getCount(); for (unsigned int currChild = 0; currChild < numChildren; currChild++) { domAny* child = daeSafeCast(any->getChildren()[currChild]); if (child) { if (strcmp(child->getElementName(), "Description" ) == 0 ) { std::string value = child->getValue(); descriptions.push_back(value); } else { OSG_WARN << "Child of element 'Descriptions' is not of type 'Description'" << std::endl; } } else { OSG_WARN << "Element 'Descriptions' does not contain expected elements." << std::endl; } } osgNode->setDescriptions(descriptions); } else { OSG_WARN << "Expected element 'Descriptions' not found" << std::endl; } } } } } domTechnique* daeReader::getOpenSceneGraphProfile(domExtra* extra) { unsigned int numTeqs = extra->getTechnique_array().getCount(); for ( unsigned int currTeq = 0; currTeq < numTeqs; ++currTeq ) { // Only interested in OpenSceneGraph technique if (strcmp( extra->getTechnique_array()[currTeq]->getProfile(), "OpenSceneGraph" ) == 0 ) { return extra->getTechnique_array()[currTeq]; } } return NULL; } // // attributes: // id, name, sid, type, layer // child elements: // 0..1 // 0..* , , , , , // 0..* // 0..* // 0..* // 0..* // 0..* // 0..* // 0..* osg::Node* daeReader::processNode( domNode *node, bool skeleton) { // First we need to determine what kind of OSG node we need // If there exist any of the , , , , , elements // or if a COLLADA_TYPE_INSTANCE_RIGID_BODY targets this node we need a MatrixTransform int coordcount = node->getRotate_array().getCount() + node->getScale_array().getCount() + node->getTranslate_array().getCount() + node->getLookat_array().getCount() + node->getMatrix_array().getCount() + node->getSkew_array().getCount(); // See if it is targeted by an animation bool targeted = false; if (node->getId()) { targeted = _targetMap[std::string(node->getId())]; } osg::Group *resultNode = NULL; bool isBone = skeleton || isJoint(node); if (coordcount > 0 || targeted || isBone) { // TODO // single matrix -> MatrixTransform // scale, euler, translate -> PositionAttitudeTransform // if targeted -> StackedTransform // otherwise a flattened -> MatrixTransform resultNode = processOsgMatrixTransform(node, isBone); } else { // No transform data, determine node type based on it's available extra data resultNode = processExtras(node); } // See if there is generic node info attached as extra processNodeExtra(resultNode, node); if (resultNode->getName().empty()) { std::string name = ""; if (node->getId()) name = node->getId(); if (node->getName()) name = node->getName(); resultNode->setName( name ); } osg::Group* attachTo = resultNode; if (!skeleton && isJoint(node)) { skeleton = true; osgAnimation::Skeleton* pOsgSkeleton = getOrCreateSkeleton(node); pOsgSkeleton->addChild(resultNode); attachTo = resultNode; resultNode = pOsgSkeleton; } // 0..* const domInstance_camera_Array& cameraInstanceArray = node->getInstance_camera_array(); for ( size_t i = 0; i < cameraInstanceArray.getCount(); i++ ) { daeElement *el = getElementFromURI( cameraInstanceArray[i]->getUrl()); domCamera *c = daeSafeCast< domCamera >( el ); if (c) addChild(attachTo, processCamera( c )); else OSG_WARN << "Failed to locate camera " << cameraInstanceArray[i]->getUrl().getURI() << std::endl; } // 0..* const domInstance_controller_Array& controllerInstanceArray = node->getInstance_controller_array(); for ( size_t i = 0; i < controllerInstanceArray.getCount(); i++ ) { osg::Node* pOsgNode = processInstanceController( controllerInstanceArray[i]); // A skin controller may return NULL, since the RigGeometry is added as // child of the skeleton and the skeleton already is added to the scenegraph if (pOsgNode) { addChild(attachTo, pOsgNode); } } // 0..* const domInstance_geometry_Array& geometryInstanceArray = node->getInstance_geometry_array(); for ( size_t i = 0; i < geometryInstanceArray.getCount(); i++ ) { addChild(attachTo, processInstanceGeometry( geometryInstanceArray[i] )); } // 0..* const domInstance_light_Array& lightInstanceArray = node->getInstance_light_array(); for ( size_t i = 0; i < lightInstanceArray.getCount(); i++ ) { daeElement *el = getElementFromURI( lightInstanceArray[i]->getUrl()); domLight *pDomLight = daeSafeCast< domLight >( el ); if (pDomLight) addChild(attachTo, processLight(pDomLight)); else OSG_WARN << "Failed to locate light " << lightInstanceArray[i]->getUrl().getURI() << std::endl; } // 0..* const domInstance_node_Array& nodeInstanceArray = node->getInstance_node_array(); for ( size_t i = 0; i < nodeInstanceArray.getCount(); i++ ) { daeElement *el = getElementFromURI( nodeInstanceArray[i]->getUrl()); domNode *n = daeSafeCast< domNode >( el ); if (n) // Recursive call addChild(attachTo, processNode( n, skeleton )); else OSG_WARN << "Failed to locate node " << nodeInstanceArray[i]->getUrl().getURI() << std::endl; } // 0..* const domNode_Array& nodeArray = node->getNode_array(); for ( size_t i = 0; i < nodeArray.getCount(); i++ ) { // Recursive call addChild(attachTo, processNode( nodeArray[i], skeleton )); } return resultNode; }