7 Getting PDF nodes unique identifiers
PDF 3D scene graph
In a 3D PDF, all entities in the 3D model are structured in a node tree representation called a 'scene graph'. The scene graph is visible in Adobe Reader in a dedicated navigation panel called "Model Tree". It can also be accessed programmatically using JavaScript. Thus, the scene graph is the accessible part of the 3D content from the PDF part of the document, and a key point is to understand the relationship between 3D entities and nodes in the scene graph.
Nodes identifier
Scene graph nodes are identified by a unique name, which may differ from the name displayed to the user. The situation is different depending on the format of the 3D stream inside the PDF:
- U3D content: the internal unique name is the same as the visible name. This is because U3D format already specifies unique node names for the tree.
- PRC content: the internal unique name is generated by Adobe Reader during PRC reading, it is the visible name suffixed by a unique identifier.
PRC entities
Not all PRC entities are represented in the scene graph. Regarding the model structure, only two kind of entities have corresponding nodes:
- Product Occurrences
- Representation Items
Warning: Product Occurrence structure may define instances of a shared sub-product structure (see the documentation on product prototypes). This kind of structure defines a lot of product occurrences that won't lead to a node in the scene graph. It is important to have a good understanding of the prototype architecture to predict the scene graph representation of the structure.
Getting nodes unique identifiers
HOOPS Publish contains functions to get the unique names defined by Adobe Reader from PRC entities. They are the function A3DAsmModelFileExportToPrcFile which should use the final A3DRWParamsPrcWriteHelper argument; and the two functions A3DEntityGetPDFNodeIdFromWrite and A3DRWParamsPrcWriteHelperFree.
Basically, the PRC first needs to be created and written to a physical file, with an helper object. Then, the model file will be traversed and entities will be interrogated with this object. Finally, the helper object will need to be deleted.
- First, create the model file.
- Save the model file physically: A3DAsmModelFileExportToPrcFile needs to be called with a final argument which is the address of a pointer on a A3DRWParamsPrcWriteHelper object. This function creates the object and fills the pointer.
- Store the A3DRWParamsPrcWriteHelper* object.
-
Traverse the model file primarily created, by parsing recursively the Product Occurrences structure:
- first traverse the part definition (from the product if it owns one, or traversing the prototypes)
- then traverse the Product Occurrences sons
- Finally, you must delete the A3DRWParamsPrcWriteHelper* object with the A3DRWParamsPrcWriteHelperFree function.
The steps required to get unique names from PRC entities are illustrated with the sample PublishPRCCube. The sample also implements product occurrence traversal for a use case with shared occurrences and prototypes.
PublishPRCCube example
The PublishPRCCube example demonstrates using the HOOPS Publish API to create geometry directly rather than load it in from file. You can also use this method if you wish to link HOOPS Publish directly to your application's internal data structures, rather than transferring geometry via file.
The work to create the cube geometry is done in the file CreatePRCAsmAndGetPDFIds.cpp, whilst PublishPRCCube.cpp creates the PDF file.
Reviewing the PRC creation code is beyond the scope of this document but the important concept of traversing the model to obtain unique ids for each node needs discussion.
The function TraverseModelForPDFIds traverses the in-memory PRC assembly structure and retrieves a PDFNodeId from each representation item using A3DEntityGetPDFNodeIdFromWrite. For this example the name of each node is checked and only the PDF Ids for each cube are returned.
The node ids that has been retrieved can be used in JavaScript to allow buttons to control the visibility of each item: the retrieved value for pcIdNode1 is used in the scene.nodes.getByName function to retrieve the node object itself:
