/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "ColladaSceneGraphCreator.h" #include "COLLADAFWVisualScene.h" #include "DbEntity.h" #include "DbBlockReference.h" #include "DbCurve.h" #include "COLLADAFWNode.h" namespace TD_COLLADA_IMPORT { SceneGraphCreator::SceneGraphCreator(DocumentImporter* documentImporter) : ImporterBase(documentImporter) {} SceneGraphCreator::~SceneGraphCreator() {} bool SceneGraphCreator::create(const COLLADAFW::VisualScene* pVisualScene) { if ( !pVisualScene ) return false; InternalNode parentNode; importNodes(pVisualScene->getRootNodes(), parentNode); return true; } void SceneGraphCreator::importNode(const COLLADAFW::Node* pNode, InternalNode& parentImportNode) { InternalNode importNode(parentImportNode.m_matTotalTransform); importNode.m_nodeId = pNode->getUniqueId(); collectTransformation(pNode, importNode); addNodeData(pNode->getUniqueId(), importNode); //TODO: import cameras? importInstanceGeometries(pNode->getInstanceGeometries(), importNode); importInstanceLights(pNode->getInstanceLights(), importNode); importInstanceControllers(pNode->getInstanceControllers(), importNode); importNodes(pNode->getChildNodes(), importNode); // Append all nodes that are referenced by this node. importInstanceNodes(pNode->getInstanceNodes(), importNode); } //------------------------------ bool SceneGraphCreator::importInstanceNodes(const COLLADAFW::InstanceNodePointerArray& instanceNodeArray, InternalNode& parentImportNode) { for ( size_t i = 0, count = instanceNodeArray.getCount(); i < count; ++i) { COLLADAFW::InstanceNode* instanceNode = instanceNodeArray[i]; const COLLADAFW::UniqueId& uniqueId = instanceNode->getInstanciatedObjectId(); OdDbObjectId objectId = getOdDbObjectIdByUniqueId(uniqueId); bool bUseBlockRefs = getDocumentImporter()->NeedToUseBlockRefs(); if (objectId.isNull() || !bUseBlockRefs) { const COLLADAFW::Node* instanciatedFWNode = getFWNodeByUniqueId(uniqueId); if (!instanciatedFWNode) continue; //We collect geometry from all child and instances nodes and create a BlockReference from this OdDbObjectIdArray nodeObjIds; if (bUseBlockRefs && getNodeRefs(uniqueId, nodeObjIds)) { OdDbObjectId tableRecId = addBlockTableRecord(nodeObjIds, OdGeMatrix3d::kIdentity); OdDbObjectId blockRefId = addBlockTableReference(tableRecId, parentImportNode.m_matTotalTransform); addUniqueIdOdDbObjectIdPair(uniqueId, blockRefId); continue; } // check if the referenced node is in one of the already received library nodes if (instanciatedFWNode) { importNode(instanciatedFWNode, parentImportNode); } } else { //The instance node already has a BlockReference, we make another one with the required matrix OdDbEntityPtr pObj = objectId.safeOpenObject(); if (pObj->isKindOf(OdDbBlockReference::desc())) { OdDbObjectId tableRecId = OdDbBlockReference::cast(pObj)->blockTableRecord(); addBlockTableReference(tableRecId, parentImportNode.m_matTotalTransform); } } } return true; } bool SceneGraphCreator::importNodes(const COLLADAFW::NodePointerArray& nodeArray, InternalNode& parentImportNode) { for (size_t i = 0, count = nodeArray.getCount(); i < count; ++i) { importNode(nodeArray[i], parentImportNode); } return true; } //------------------------------ template bool SceneGraphCreator::importInstances(const COLLADAFW::PointerArray& instanceArray, InternalNode& parentImportNode, void (SceneGraphCreator::*postProcess)(Instance*, OdDbObjectId, const COLLADAFW::UniqueId&)) { for (size_t idxInstance = 0, count = instanceArray.getCount(); idxInstance < count; ++idxInstance) { Instance* instance = instanceArray[idxInstance]; const COLLADAFW::UniqueId& uniqueId = instance->getInstanciatedObjectId(); const COLLADAFW::Controller* pController = getFWControllerByUniqueId(uniqueId); SubUniqueIdIter first, last; if (pController) { COLLADAFW::UniqueId prevId(pController->getSource()); COLLADAFW::UniqueId currId(uniqueId); while (pController->getSource().getClassId() == COLLADAFW::COLLADA_TYPE::CONTROLLER && prevId != currId) { prevId = pController->getSource(); pController = getFWControllerByUniqueId(prevId); currId = pController->getSource(); } getSubUniqueIdsByUniqueId(pController->getSource(), first, last); } else { getSubUniqueIdsByUniqueId(uniqueId, first, last); } for (SubUniqueIdIter it = first; it != last; ++it) { COLLADAFW::UniqueId subUniqueId = it->second; OdDbObjectIdArray arrObjIds; getOdDbObjectIdArrByUniqueId(subUniqueId, arrObjIds); ODA_ASSERT_ONCE(!arrObjIds.isEmpty()); for (OdDbObjectId& oldObjectId : arrObjIds) { OdDbEntityPtr pEnt = oldObjectId.safeOpenObject(); if (pBTRTmp.isNull()) { pBTRTmp = pEnt->database()->getModelSpaceId().safeOpenObject(OdDb::kForWrite); } bool bUseBlockRefs = getDocumentImporter()->NeedToUseBlockRefs(); OdDbObjectId objectId; if (!bUseBlockRefs || (objectId = importAsBlockRef(subUniqueId, pEnt, parentImportNode)).isNull()) { OdDbEntityPtr pClone = pEnt->clone(); objectId = pBTRTmp->appendOdDbEntity(pClone); pClone->transformBy(parentImportNode.m_matTotalTransform); addToModelSpaceMap(subUniqueId, oldObjectId, objectId, parentImportNode); } //add OdDbObjectId to Node addOdDbObjectToNode(parentImportNode.m_nodeId, OdGeMatrix3d::kIdentity, objectId); // post process the creation if (postProcess) (this->*postProcess)(instance, objectId, subUniqueId); } } } return true; } bool SceneGraphCreator::importInstanceGeometries(const COLLADAFW::InstanceGeometryPointerArray& instanceGeometryArray, InternalNode& parentImportNode) { return importInstances(instanceGeometryArray, parentImportNode, &SceneGraphCreator::storeMaterialBinding); } bool SceneGraphCreator::importInstanceLights(const COLLADAFW::InstanceLightPointerArray& instanceLightArray, InternalNode& parentImportNode) { return importInstances(instanceLightArray, parentImportNode); } bool SceneGraphCreator::importInstanceControllers(const COLLADAFW::InstanceControllerPointerArray& instanceControllerArray, InternalNode& parentImportNode) { return importInstances(instanceControllerArray, parentImportNode, &SceneGraphCreator::storeMaterialBinding); } OdDbObjectId SceneGraphCreator::importAsBlockRef(const COLLADAFW::UniqueId& subUniqueId, OdDbEntityPtr pBLockEnt, const InternalNode& parentImportNode) { if (pBLockEnt.isNull()) return OdDbObjectId::kNull; OdDbObjectId msObjectId; InternalNode msIntNode; if (!getModelSpaceEntData(subUniqueId, pBLockEnt->objectId(), msObjectId, msIntNode)) return OdDbObjectId::kNull; OdDbObjectId blockRefId; //If we find an object in Model Space with such a geometry instance: //1. If it is not a BlockReference, then use its BlockTableRecord to create another BlockReference //2. If BlockReference, then create a new BlockTableRecord and 2 BlockReference OdDbEntityPtr pEntMS = msObjectId.safeOpenObject(); if (!pEntMS->isKindOf(OdDbBlockReference::desc())) { OdDbObjectIdArray objArr; objArr.append(pEntMS->objectId()); OdDbObjectId tableRecId = addBlockTableRecord(objArr, OdGeMatrix3d(msIntNode.m_matTotalTransform).invert()); OdDbObjectId blockRefFromOldId = addBlockTableReference(tableRecId, msIntNode.m_matTotalTransform); //We need to update the material map to replace the object with BlockReference //updateMaterialData(msObjectId, blockRefFromOldId); addOdDbObjectToNode(msIntNode.m_nodeId, OdGeMatrix3d::kIdentity, blockRefFromOldId); blockRefId = addBlockTableReference(tableRecId, parentImportNode.m_matTotalTransform); addToModelSpaceMap(subUniqueId, pBLockEnt->objectId(), blockRefId, parentImportNode); } else { OdDbObjectId pTableRecId = OdDbBlockReference::cast(pEntMS)->blockTableRecord(); blockRefId = addBlockTableReference(pTableRecId, parentImportNode.m_matTotalTransform); } return blockRefId; } OdDbObjectId SceneGraphCreator::addBlockTableReference(OdDbObjectId tableRecId, const OdGeMatrix3d& transform, OdDbBlockTableRecord* pBTR) { OdDbBlockReferencePtr pBlockRef = OdDbBlockReference::createObject(); pBlockRef->setBlockTableRecord(tableRecId); pBlockRef->setBlockTransform(transform); if (pBTR) return pBTR->appendOdDbEntity(pBlockRef); return pBTRTmp->appendOdDbEntity(pBlockRef); } OdDbObjectId SceneGraphCreator::addBlockTableRecord(const OdDbObjectIdArray& objArr, const OdGeMatrix3d& transform) { OdDbBlockTableRecordPtr pNewBTR = OdDbBlockTableRecord::createObject(); pNewBTR->setName(odrxSystemServices()->createOdGUID().toString(OdGUID::StringFormat::Digits).c_str()); OdDbBlockTablePtr pTable = getDocumentImporter()->getDatabase()->getBlockTableId().safeOpenObject(OdDb::kForWrite); OdDbObjectId pTableRecId = pTable->add(pNewBTR); for (const OdDbObjectId& id : objArr) { if (id.isNull() || id.isErased()) continue; OdDbEntityPtr pObj = id.safeOpenObject(OdDb::kForWrite); pObj->transformBy(transform); } pNewBTR->assumeOwnershipOf(objArr); return pTableRecId; } bool SceneGraphCreator::getNodeRefs(const COLLADAFW::UniqueId& uniqueId, OdDbObjectIdArray& nodeObjIds) { const COLLADAFW::Node* pNode = getFWNodeByUniqueId(uniqueId); if (!pNode) return false; OdDbObjectIdArray nodeObjMSArr; InternalNode intNode; if (!getNodeData(uniqueId, intNode, nodeObjMSArr)) return false; OdDbObjectIdArray nodeGraphObjectsIds; OdDbObjectId objectId = getOdDbObjectIdByUniqueId(uniqueId); if (!objectId.isNull()) { OdDbEntityPtr pObj = objectId.safeOpenObject(); if (pObj->isKindOf(OdDbBlockReference::desc())) { OdDbObjectId tableRecId = OdDbBlockReference::cast(pObj)->blockTableRecord(); OdDbObjectId blockRefId = addBlockTableReference(tableRecId, OdGeMatrix3d::kIdentity); nodeGraphObjectsIds.append(blockRefId); } } else if (!nodeObjMSArr.isEmpty()) { OdDbObjectId tableRecId = addBlockTableRecord(nodeObjMSArr, OdGeMatrix3d(intNode.m_matTotalTransform).invert()); OdDbObjectId newBlockRefId = addBlockTableReference(tableRecId, intNode.m_matTotalTransform); addUniqueIdOdDbObjectIdPair(uniqueId, newBlockRefId); OdDbObjectId blockRefId = addBlockTableReference(tableRecId, OdGeMatrix3d::kIdentity); nodeGraphObjectsIds.append(blockRefId); } const COLLADAFW::NodePointerArray& childArr = pNode->getChildNodes(); for (size_t n = 0; n < childArr.getCount(); n++) { if (!getNodeRefs(childArr[n]->getUniqueId(), nodeGraphObjectsIds)) return false; } const COLLADAFW::InstanceNodePointerArray& instArr = pNode->getInstanceNodes(); for (size_t n = 0; n < instArr.getCount(); n++) { if (!getNodeRefs(instArr[n]->getInstanciatedObjectId(), nodeGraphObjectsIds)) return false; } for (const OdDbObjectId& id : nodeGraphObjectsIds) { if (id.isNull() || id.isErased()) continue; OdDbEntityPtr pEnt = id.safeOpenObject(OdDb::kForWrite); pEnt->transformBy(intNode.m_matNodeTransform); } nodeObjIds.append(nodeGraphObjectsIds); return true; } void SceneGraphCreator::eraseObjects(const OdDbObjectIdArray& objArr) { for (const OdDbObjectId& id : objArr) { if (id.isNull() || id.isErased()) continue; OdDbEntityPtr pEnt = id.safeOpenObject(OdDb::kForWrite); pEnt->erase(); } } void SceneGraphCreator::collectTransformation(const COLLADAFW::Node* pNode, InternalNode& parentImportNode) { COLLADABU::Math::Matrix4 transformationMatrix; pNode->getTransformationMatrix(transformationMatrix); for(int i = 0; i < 4; ++i) { for(int j = 0; j < 4; ++j) { parentImportNode.m_matNodeTransform[i][j] = transformationMatrix[i][j]; } } parentImportNode.m_matTotalTransform = parentImportNode.m_matTotalTransform * parentImportNode.m_matNodeTransform; } }