This guide explains how to use the HOOPS/Parasolid classes to integrate the Parasolid Kernel Modeller with HOOPS/3dGS and HOOPS/MVO. It assumes familiarity with the Parasolid Kernel Modeller, HOOPS/3dGS, and HOOPS/MVO.
The HOOPS/MVO Parasolid, source code, export library (for Windows)and libraries/dlls are located in the /dev_tools/hp_bridge subdirectories. The /dev_tools/hp_bridge/source directory includes the Microsoft Visual Studio Project files or necessary make files to enable rebuilding.
To start Parasolid and the HOOPS/Parasolid Integration module, call HP_Init in an application initialization routine. This function initializes the Parasolid kernel and registers the HOOPS-specific implementation of the Parasolid GO_Routines. All geometry rendered via Parasolid's PK_TOPOL_render_XXX routines will be mapped to corresponding segments and geometry in the HOOPS graphics database. (HOOPS does not need an explicit initialization routine, as it is automatically initialized during the first call of a HOOPS function.)
Note that the HP_Init function requires a Schema path and Boolean value. The Schema path instructs Parasolid where to look for some utility files necessary to read in .x_t files. The schema files are located on the CD installation in the /parasolid/schema directory.
The Boolean should be set to false, unless you are working with Tech Soft 3D to test a new automatic update feature that keeps HOOPS and Parasolid continuously in synchronization.
To shutdown Parasolid as well as the HOOPS/Parasolid module, call HP_Close. This should be called in an application cleanup routine, when Parasolid is no longer needed. This does not shutdown the HOOPS Graphics System - the HOOPS function HC_Reset_System will still need to be called to directly shutdown HOOPS:
To read a Parasolid file, call HP_Read_Xmt_File. This parses the specified Parasolid file, populates the Parasolid modeling kernel with its contents, and then maps the model to a corresponding HOOPS segment tree hierarchy that includes all geometry and attributes.
The HOOPS/Parasolid Reference Application contains an example of how to use this function. Refer to the HParaModel::Read function located in HSolidModel.cpp.
You can control facet tessellation as an import argument. In HP_Read_Xmt_File, use the face_tessellation_level parameter to specify tessellation. Valid options are as follows:
"Custom" is the default, which means the developer is responsible for setting all the tessellation options himself. You can also set tessellation options in HP_Render_Entity and HP_Render_Entities. Examples of the effect of the tessellation are shown below:
Rendering Parasolid entities requires a sequence of steps, most of which are encapsulated by the HOOPS/Parasolid Integration functions.
The HOOPS/Parasolid Integration fully encapsulates the first two items. Additionally, when a Parasolid file is read via the HP_Read_Xmt_File() function, all the necessary PK_TOPOL_render_xxx calls are made automatically as each Parasolid part/entity is parsed. However, the HP_Render_Entity routine needs to be manually called when Parasolid entities are being dynamically created and edited via the application's user-interface and underlying logic.
As an example, we will go through the process for inserting a cylinder object into Parasolid, and passing the tessellated information to HOOPS for subsequent rendering and interaction. This and future sections assume familiarity with the types of Parasolid entities, denoted by the Parasolid type PK_CLASS_t.
A cylinder, along with many other predefined Parasolid objects, is represented by a PK_BODY entity, and is created via the routine:
Parasolid entities are referred to by a 'TagID', so this function populates the Parasolid kernel with a cylinder of the specified radius and basis_set, and returns the TagID of the associated Parasolid body. However, the cylinder will not actually be rendered (mapped to corresponding HOOPS segments and geometry) until a call is made to the HP_Rendering_Entity() routine, discussed in the next section.
The following code demonstrates how to create a Parasolid cylinder. Note that it calls HP_Render_Entity().
After a Parasolid entity is defined and inserted into the Parasolid database,
the tessellated geometric information that represents the entity must be generated.
PK_BODY entities (along with many others) are composed of PK_FACE and PK_EDGE
entities. These entities denote the logical surface and edge/boundary
information associated with the body. Specifically, a cylinder body object
is composed of three PK_FACE entities, one for the top, one for the bottom,
and one for the 'tubular' part of the cylinder. It is composed of
two PK_EDGE entities, one for a circle denoting the top edge, and one for a
circle denoting the bottom edge. The tessellated geometric information
associated with the PK_FACE and PK_EDGE entities would typically need to be
generated by manually calling the following Parasolid routines:
In addition to tessellating the body into facets and lines, these functions will send the tessellated information to the Graphical Output routines.
The HOOPS/Parasolid Integration includes a function which automatically makes the above PK_TOPOL_render_facet calls, and because the HOOPS/Parasolid Integration implements the underlying GO routines, the geometric data that represents the cylinder is automatically mapped to a group of geometric primitives in the HOOPS database. (By default, these primitives are inserted in the currently open HOOPS segment. The HP_Set_Rendering_Options function discussed later in this document, provides for control of this behavior, including creation of subsegments and optimization of the geometry.)
To render Parasolid entities and have their tessellated representation automatically mapped to HOOPS graphical primitives, you may simply call:
where 'count' is the number of entities, and 'entities' is the array of Parasolid
entities (of type PK_Entity_t). This function also updates the bi-directional
mapping (which uses a hash table) maintained by the HOOPS/Parasolid integration.
Recall that the HOOPS geometry and any additional segments generated as a result of the HP_Render_Entity and HP_Render_Entities routines will be placed in the currently open HOOPS segment. The following code ties together the code necessary to insert a cylinder object into both Parasolid and HOOPS. It assumes that we have obtained the creation parameters for a cylinder from the UI, opens a segment and then calls the 'CreateSolidCylinder' function discussed previously. After CreateSolidCylinder is called (which calls HP_Render_Entity, discussed above), the Parasolid database will contain a parametric representation of the cylinder (a Parasolid 'body'), and the HOOPS database will contain the corresponding geometric representation (a HOOPS 'shell'):
The Parasolid 'body' entity that represents the cylinder is accessible via the 'cyl_tag' TagID. Details on how to identify the HOOPS geometric primitives associated with the Parasolid entity are covered later in this document, in the section entitled 'Operating on Parasolid Entities'.
The HOOPS/Parasolid Reference Application implements and uses the platform-independent creation/rendering functions discussed above. It also includes additional geometry creation functions:
- The HOpCreateSolidCone, HOpCreateSolidCylinder and HOpCreateSolidSphere classes are all derived from the HBaseOperator MVO class, and provide for creation of 'rubberband' construction geometry in addition to creation/rendering of the Parasolid and HOOPS entities. They contain implementations of CreateSolidCone, CreateSolidCylinder, and CreateSolidSphere functions as outlined above.
We will recall that the HOOPS/Parasolid Integration maintains a mapping between Parasolid entities and HOOPS Geometry, which enables the developer to determine the Parasolid entities associated with a given HOOPS geometric primitive or segment, and vice versa. The HP_Render_Entities function causes this mapping to be created/updated each time it is called to populate the HOOPS database with the tessellated representation of Parasolid entities.
As a result, another HOOPS/Parasolid function must be called when deleting HOOPS geometry associated with Parasolid entities to ensure that the internal mapping is kept up to date. The function is called HP_Delete_Entity_Geometry, and would be used to delete the HOOPS geometry associated with a Parasolid entity after the latter is deleted. For example, the following function is a wrapper function which deletes a Parasolid entity along with its associated HOOPS geometry:
It is important to use the HP_Delete_Entity_Geometry function whenever deleting HOOPS geometric primitives associated with deleted Parasolid entities. If only the normal HOOPS/3DGS deletion functions were called (HC_Delete_By_Key, etc...), then the HOOPS/Parasolid mapping would not be correct, and problems would occur during subsequent operations.
A call to HP_Delete_Entity_Geometry() normally deletes all associated entities from HOOPS including segments. Sometimes however it is desirable to preserve the segment structure if e.g. extra information with no connection to the solid modeller has been previously associated to a body. To allow that there is a third parameter in the call to HP_Delete_Entity_Geometry() called "PreserveSegments". If this boolean is set to "true" (it defaults to false) then only true HOOPS geometry is deleted and keys that are associated with segments are disregarded. This means that if a body is deleted it will be retesselated into the same segment again on the next call to HP_Render_Entity().
However, let's consider the following scenario. Say we have a simple cylinder
body which contains (among other things) two edges. After normal tesselation
we want to move one of the edges (consisting of 2 HOOPS ellipses) into a separate
segment (let's call it "special") which contains e.g. some text annotation and
a different line weight attribute. We can do this with HC_Move_By_Key().
Unfortunately after a retesselation (even when using HP_Delete_Entity_Geometry
with PreserveSegments = true) the two HOOPS ellipses are recreated in their
default segment again and the newly created "special" segment is empty.
To avoid this HP_Associate_Entity_To_Key() can be used which associates a HOOPS keys to a Parasolid entity. In our example we use it to associate the key of the "special" segment to the Parasolid edge entity that we want to store underneath it. Whenever the Parasolid body needs to be rerendered we delete it's associated geometry by using "HP_Delete_Entity_Geometry" with the PreserveSegment flag set to true. This way even though all the HOOPS geometry of the body is deleted there is still the connection to the "special" segment in the database.
Here is some example code:
HP_Render_Entity() and HP_Render_Entities() have a third parameter which is a Parasolid transformation entity (0 by default) that represents the view matrix for view dependent tessellation. It is the responsibility of the caller to set the appropriate tessellation options for the frustum routines. This example demonstrates how to temporarily overwrite tesselation options to render the body in a view dependent way.
As mentioned in the previous section, the HP_Render_Entity function instructs HOOPS to insert the tessellated geometric primitives into the currently open HOOPS segment. The methods by which the HOOPS/Parasolid Integration maps the tessellated representation of Parasolid entities to HOOPS segments and geometric primitives are controlled using the function HP_Set_Rendering_Options. (Note: this function is different than the HOOPS/3DGS function called HC_Set_Rendering_Options, which controls how the HOOPS 3D Graphics Systems performs 2d/3d rendering.)
The default mapping behavior will create additional HOOPS subsegments within the currently open segment for each Parasolid body being rendered. Because each HOOPS segment has one opinion about each HOOPS attribute (color, line_pattern, modeling matrix, etc...), such a segment hierarchy facilitates applying unique sets of attributes to the group of geometric primitives that represent a Parasolid body. (The new attribute can be directly set on the unique HOOPS segment associated with the body.)
The default mapping also preserves any color attributes which may have been stored in the Parasolid kernel by creating a HOOPS segment for each unique color in the Parasolid kernel. These segments are created underneath each body segment. Creating multiple segments for each color is again necessary due to the attribute coherence rule of a HOOPS segment. Though some HOOPS geometric primitives (shells and meshes) can be individually colored, the other primitives are rendered using the color and other attributes set in the containing segment.
The Integration also maps each Parasolid PK_FACE entity, which are rendered as a result of the PK_TOPOL_render_facet call, to a HOOPS 'shell' primitive by default. (The alternative is to map multiple PK_FACE entities to a single HOOPS shell, which is supported by the "merge faces" option discussed later in this section.) Mapping each PK_FACE to a single HOOPS shell facilitates Parasolid operations that work at the level of a PK_FACE; it is easier to access an entire HOOPS shell, rather than access a subset of edges/faces/vertices within a HOOPS shell. Each PK_EDGE entity is mapped to one or more HOOPS 'line' primitives, which include 'line', 'polyline', 'arc', 'circular arc', 'ellipse', and 'elliptical arc'.
The following diagram depicts how two Parasolid bodies, each containing two different colors, would be mapped to the HOOPS database using the default Mapping_Options; the 'part' segment denotes the currently open segment at the time the PK_TOPOL_render_XXX calls are made:
Setting the Rendering_Option "preserve color = off" instructs HOOPS to ignore any color attributes stored in the Parasolid kernel when mapping Parasolid entities to the HOOPS database. HOOPS will not create subsegments within the currently open segment for each unique color:
Color attributes are frequently stored in Parasolid files, hence color should typically be preserved when reading in a file. ( HP_Read_Xmt_File() ) Additionally, when dynamically creating/inserting a Parasolid entity, it is useful to set the entity's desired color inside of Parasolid so that the color will be stored along with the entity when writing out an Xmt file. In this common situation, color should also be preserved so that the corresponding HOOPS segments/geometry will be rendered with the correct color. However, if there is no need to either create entities with different/specific colors, or to preserve color information contained in a file, "preserve color = off" may be used.
Setting the Mapping_Option "create body segments = off" instructs HOOPS to not create extra subsegments within the currently open segment for each body.
Because each HOOPS segment has one opinion about each HOOPS attribute (color, line_pattern, modeling matrix, etc...), such a segment hierarchy does not allow for attribute granularity among the various geometric primitives that represent the Parasolid geometry rendered within the currently open HOOPS segment. It only allows attributes to be set based on the color partitioning of the segments. Any HOOPS attribute that is set on the 'red' segment in the above example will affect all geometry in the segment; hence all bodies that are being represented will be affected equally by the attribute change. If a body needed to have a unique modeling matrix applied to it, its associated HOOPS geometry would have to be identified via the HP_Compute_TagID and HP_Compute_Geometry_Keys() functions, and moved into a separate segment that contained the new modeling matrix. In contrast, if the default segment structure outlined in the beginning of this section was used, the new attribute could be directly set on the unique HOOPS segment associated with the body.
Setting the Mapping_Options "merge faces = on" instructs the Integration to merge all the HOOPS shells that were generated to represent a PK_BODY (one shell for each PK_FACE) into a single HOOPS shell. In the case of a multi-colored PK_BODY, the shells corresponding to PK_FACES that have the same color attribute will be merged into one HOOPS shell, resulting in as many HOOPS shells are there are distinct colors in the PK_BODY entity.
This mapping results in optimal rendering speed at the expense of ease of manipulating individual PK_Body entities. Applications that are concerned only with viewing, or that do not need to select at the PK_FACE level (to perform 'face-level' Parasolid operations) will gain significant rendering speed advantages by making use of this feature.
When performing operations that modify Parasolid entities, it is necessary to keep the HOOPS representation of the model synchronized with its Parasolid representation. Because such operations are typically initiated by selecting (hit-testing) HOOPS geometric objects, it is necessary to determine what Parasolid entity(s) correspond with the selected HOOPS entities, and vice versa. The HOOPS/Parasolid integration includes routines which support this requirement.
HOOPS geometric entities are identified by HOOPS 'keys'. Keys are renumbered within the integration code to have Parasolid-specific values after being created as a result of the Parasolid PK_TOPOL_render_XXX routines. Recall that Parasolid entities are referred to by a Parasolid 'tagID'. Given a HOOPS key, the HP_Compute_TagID function will return the Parasolid tagID for the Parasolid entity that the specified HOOPS geometric entity is associated with. Conversely, given a Parasolid tagID, the HP_Compute_Geometry_Keys function will return all the HOOPS keys for the tessellated geometry or the HOOPS segment associated with the Parasolid entity. (Recall that a Parasolid entity might be represented by more than one HOOPS geometric entity.)
Selection on the HOOPS scene returns the key(s) of HOOPS geometry, requiring us to find the Parasolid entity that the selected HOOPS geometry is associated with. If we wanted to access the Parasolid 'body' entity given the key of a selected HOOPS geometric primitive, we would call:
We can now use the tagID of the Parasolid entity to perform Parasolid operations, or to access the HOOPS geometric primitives which represent the Parasolid entity.
Highlighting a Parasolid entity involves performing a selection on the HOOPS scene and highlighting the HOOPS geometry associated with the Parasolid entity. After identifying the Parasolid entity associated with the selected HOOPS geometry (described in the previous section), we need to access the HOOPS geometry or segment which represents the entity.
In the following code example, we compute all the HOOPS geometry that is associated with the 'edges' and 'faces' of the selected Parasolid entity. This may also be referred to as 'Parasolid body-level selection'. (Recall that Parasolid entities are composed of PK_FACE and PK_EDGE entities, whose tessellated representations are sent to HOOPS via the HP_Render_Entity call. HP_Render_Entity encapsulates calls to the PK_TOPOL_render_facet and PK_TOPOL_render_line routines.)
This returns the number of HOOPS keys associated with the given Parasolid entity, and places their values into the 'keys' array. Alternately, the call to HP_Compute_Geometry_Key_Count could be omitted, and HP_Compute_Geometry_Keys could be called using the 'max_count' parameter, which specifies the maximum number of keys desired. In the following code example, the last argument in the function call instructs HOOPS to limit the number of returned keys to 40000:
If the mapping option "create body segments" is true and we are dealing with a Parasolid 'body', we know that there is a unique HOOPS segment which contains all the geometry associated with a Parasolid 'body' entity. Therefore, in this case we can simply ask for the key of the HOOPS segment associated with the body and highlight the entire segment, rather than individually compute and highlight each geometric primitive:
The HOOPS primitives can be highlighted by moving them from their current HOOPS segment into a segment with different rendering attributes (color, linestyle, etc...). A HOOPS segment can be highlighted by directly modifying its rendering attributes. Using the HOOPS/MVO HSelectionSet class as a base, we can utilize defined geometry and segment selection (highlight) methods to create a Parasolid-application-specific SelectionSet class and 'Select' function as follows.
This derived class will not only support body level selection, but will also support several 'selection levels' as indicated by the m_SelectLevel variable. It also adds another variable of type HVarray, in order to store the list of selected Parasolid entities; the base class only deals with storing the list of selected HOOPS primitives that are associated with the selected Parasolid entity.
The HOOPS/Parasolid Reference Application contains an implementation of HPSelectionSet, which includes the above Select function and definition of the m_SelectLevel type. Refer to HSSelectionSet.h and HSSelectionSet.cpp for the interface and implementation of the class.
Identification, and selection (highlighting) of the HOOPS geometry associated with a particular Parasolid entity is usually an intermediate step in performing a useful modeling operation. This section provides an example of how to perform a Boolean operation.
The Parasolid Boolean function, PK_BODY_Boolean, operates at the level of a Parasolid 'body'. It requires the definition of a 'target' body entity, the 'tool' body entities that will operate on the target, and a description of the Boolean operation to be performed. After the function is called, the HP_Render_Entity function needs to be called to send the tessellated representation of the final target body to HOOPS. (See the section on Creating and Rendering Parasolid Entities.)
The syntax of the function is as follows:
The result of the Boolean operation is returned as an array of bodies. We can create a utility function called 'parasolid_Boolean', which encapsulates the Parasolid Boolean function call and associated rendering function call as follows.
Recall that the HOOPS geometric primitives that are created as a result of the HP_Render_Entity function will be placed into the currently open HOOPS segment, requiring us to open a segment first. Additionally, given a list of selected HOOPS geometry, we need to determine the corresponding Parasolid 'target' and 'tool' entities to be passed to the 'parasolid_Boolean' function. The following function is a method of the HParaView class (HSolidView.h and HSolidView.cpp), which is part of the HOOPS/Parasolid Reference Application. It performs the following operations:
Many other Parasolid modeling operations that delete and create new Parasolid entities would require a similar procedure of identifying and deleting the HOOPS geometric primitives associated with the Parasolid entities. In any case, the process outlined above must be followed through in order to synchronize the HOOPS database's segments and primitives with the Parasolid kernel.
Other Parasolid operations on Parasolid entities may not require deletion of corresponding HOOPS geometry, but rather they may require that a HOOPS attribute be modified to keep the two systems in sync. For example, a transformation to a Parasolid BODY (using PK_BODY_transform) needs to be reflected in HOOPS by setting the corresponding HOOPS modeling matrix in the segment containing the geometric representation of the BODY. Such an operation is handled in the HOOPS/Parasolid Reference Application's HPOpObjectTranslate class. (HPOpObjectTranslate.h and HPOpObjectTranslate.cpp).