Exporting Files
HOOPS Visualize can export a few different types of file formats. Regardless of the file type, a similar pattern is employed. For this section of the Programming Guide, we’ll cover HSF file export (the native file format for HOOPS Visualize) as well as export to HTML. See the Programming Guide sections on Exchange integration and Parasolid integration for information on exporting via HOOPS Exchange and Parasolid, respectively.
HSF Export
When exporting data, HSFs can be written from any arbitrary segment. This means that the entire scene can be saved by writing from the window segment. Alternatively, a portion of the scene can be written if only a piece of a model needs to be saved. Note that when saving partial scenes, attributes inherited by the branch root are not saved with the HSF. If you wish to save attributes into the HSF, they must be explicitly set within the branch you are exporting.
The basic method for exporting a model to HSF is shown below:
try {
HPS::Stream::ExportOptionsKit exportOptionsKit;
exportOptionsKit.SetColorCompression(true, 16);
HPS::Stream::File::Export(filename, mySegmentKey, exportOptionsKit).Wait();
}
catch (IOException ioe) {
// handle exception
throw;
}
try
{
HPS.Stream.ExportOptionsKit exportOptionsKit = new HPS.Stream.ExportOptionsKit();
exportOptionsKit.SetColorCompression(true, 16); // color compression ranges from 0 to 72
HPS.Stream.File.Export(filename, mySegmentKey, exportOptionsKit).Wait();
}
catch (HPS.IOException ioe)
{
// handle exception
}
The HPS::Stream::ExportOptionsKit
has a variety of lossy compression options. Color compression is enabled in the snippet above as an example of how to specify compression.
IMPORTANT: All file exports are done synchronously, except for HSF stream files. Do not try to export on a separate thread while the scene graph can be modified, because the scene graph must be in a write-locked state during the export.
For HSF files, the HPS::Stream
export operation will happen on a separate thread. Do not modify the subtree while the export is occurring. When exporting stream files, you should use a HPS::Stream::ExportNotifier
to control the export operation. For example, you can call:
Status()
, to receive the current export status and check the progress.Wait()
, to block the current thread until the export operation is finished.Cancel()
, to cancel the export operation.
If you cancel a file export, you are responsible for cleaning up the partially exported file on disk.
Several export options apply. Our HSF writer offers compression options which can be enabled or disabled for certain situations. All options are disabled by default.
Vertex compression
This involves encoding the locations of shell vertices, providing reduction in file size in exchange for loss of coordinate precision and slightly lower visual quality. The degradation in visual quality is highly dependent on the topology of the shell, as well as how the normals information is being exported.
Vertex compression can be enabled by setting the HPS::Stream::ExportOptionsKit::SetVertexCompression
parameter.
Note
Vertex compression can cause precision issues when writing HSF files containing textures. If you are experiencing this problem, or think you may experience it, consider turning off position and parameter compression any time you are writing an HSF while using a texture.
A less draconian approach would depend on the type of texture(s) associated with the HSF. High-contrast, distinct edges (checkerboard, stripes) favor disabling compression, while general photographic textures (landscapes, materials like wood grain) can get away with minor compression-based distortion.
Normals compression
Normals compression involves encoding vertex normals, providing reduction in file size in exchange for lower visual quality. Again, the degradation in visual quality is highly dependent on the topology of the shell, as well as how the normals information is being exported. HOOPS/Stream transmits compressed normals for vertices that have been compressed, or if a normal has been explicitly set on a vertex. Surfaces that had gradual curvature over a highly tessellated region can look faceted due to the aliasing of the compressed normals. The function HStreamFileToolkit::SetNumNormalBits
allows the developer to greatly reduce or effectively remove such aliasing at the cost of transmitting more data per normal. The default is 10.
Normals compression can be enabled by setting the HPS::Stream::ExportOptionsKit::SetNormalsCompression
parameter.
Parameter compression
This involves encoding the vertex parameters (texture coordinates). Compression will reduce the file size but this could also result in the loss of precision in textures mapping.
Parameter compression can be disabled by setting the HPS::Stream::ExportOptionsKit::SetParameterCompression
parameter.
Connectivity compression
This compresses ‘shell’ connectivity information. This compression technique can provide compelling reductions in files sizes for datasets that contain many ‘shell’ primitives, but can also be a computationally intensive algorithm depending on the size of individual shells. Developers will need to decide for themselves whether the reduced file size is worth the extra computation time.
Additionally, depending on the topology of the shell, the algorithm may provide limited compression benefit or have to ‘punt’ after performing substantial work, thereby providing little to no additional file size reduction in exchange for extra computation time. Therefore, developers should do some experimentation with their specific class of datasets to see if the option is buying them any reduction in file size. If files sizes for typical files are the same both with and without the option set, then this compression option should be disabled when exporting an HSF file. Some specific examples of when the algorithm will punt or perform poorly are shells that contain many backwards faces (which also impact rendering performance and should generally be avoided anyway!), or contain certain complex combinations of ‘handles’ (a teapot or a torus each have one handle) and holes (for example, a flat plate that has a hole in the middle). In general, the connectivity compression algorithm will perform well with most of these cases, but developers should still take some time to investigate the [extra export time vs. file-size reduction] of their datasets with and without this option enabled.
Connectivity compression can be enabled by setting the HPS::Stream::ExportOptionsKit::SetConnectivityCompression
parameter.
Exporting to a Buffer
In addition to saving to an HSF, the HPS::Stream class supports exporting HSF data to a buffer. This is implemented as an overload to HPS::Stream::File::Export
.
HPS::ByteArrayArray buffers;
try {
HPS::Stream::ExportOptionsKit exportOptionsKit;
// write to a series of buffers
HPS::Stream::ExportNotifier exportNotifier = HPS::Stream::File::Export(mySegmentKey, exportOptionsKit, buffers);
// pauses this thread until the HSF is finished exporting
exportNotifier.Wait();
}
catch (HPS::IOException ioe) {
// handle exception
throw;
}
byte[][] buffers;
try
{
HPS.Stream.ExportOptionsKit exportOptionsKit = new HPS.Stream.ExportOptionsKit();
// write to a series of buffers
HPS.Stream.ExportNotifier exportNotifier = HPS.Stream.File.Export(mySegmentKey, exportOptionsKit, out buffers);
exportNotifier.Wait();
}
catch (HPS.IOException ioe)
{
// handle exception
throw;
}
Export Events
When writing an HSF file, Visualize can notify you of its progress through the use of HPS::Stream::SegmentExportEvent
and HPS::Stream::GeometryExportEvent
. You receive these events by setting an HPS::Stream::ExportEventHandler
for the HPS::Stream::ExportEvent
type (from HPS::Object::ClassID
) on your HPS::Stream::ExportOptionsKit
. The HPS::Stream::ExportEventHandler
class has a virtual function, Handle, that Visualize will call with the HPS::Stream::ExportEvent
data when it is about to write the provided data. You should override the Handle function to inspect the data to suit your needs.
The following code sample defines a custom ExportEventHandler
class, which will export some non-db user data if a specific segment-name is encountered in the HSF file:
class SpecialExportEvent: public HPS::Stream::ExportEventHandler {
public:
void Handle(HPS::Stream::ExportEvent* e)
{
if (e != nullptr) {
HPS::Stream::SegmentExportEvent* seg_event = static_cast<HPS::Stream::SegmentExportEvent*>(e);
HPS::UTF8 name = seg_event->segment_key.Name();
if (name == "My_Special_Segment") {
char const* str = "HOOPS! HOOPS!";
seg_event->non_db_user_data.assign(str, str + strlen(str));
}
}
}
};
class SpecialExportEvent : HPS.Stream.ExportEventHandler
{
public override void Handle(HPS.Stream.ExportEvent e)
{
HPS.Stream.SegmentExportEvent seg_event = (HPS.Stream.SegmentExportEvent)e;
string name = seg_event.segment_key.Name();
if (name == "My_Special_Segment")
{
string str = "HOOPS Visualize!";
seg_event.non_db_user_data = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, seg_event.non_db_user_data, 0, seg_event.non_db_user_data.Length);
}
}
};
We then set the custom handler on the HPS::Stream::ExportOptionsKit
for the segment-export event, prior to exporting the HSF File:
SpecialExportEvent special_export_event;
HPS::Stream::ExportOptionsKit export_options;
export_options.SetEventHandler(special_export_event, HPS::Object::ClassID<HPS::Stream::SegmentExportEvent>());
SpecialExportEvent special_export_event = new SpecialExportEvent();
HPS.Stream.ExportOptionsKit export_options = new HPS.Stream.ExportOptionsKit();
export_options.SetEventHandler(special_export_event, HPS.Object.ClassID<HPS.Stream.SegmentExportEvent>());
User Data
User data can be exported to an HSF file in two different ways. One form is user data that is specified within the database and is associated with a segment or geometry (see User data), and will be automatically exported along with the database. The second form is user-data that is not associated with the database and can be exported by utilizing the HPS::Stream::ExportEvent.non_db_user_data
, where you may copy arbitrary data.
HTML Export
Please note, HTML Export is only available for 64-bit Windows applications.
To export your model to HTML, first import it into Visualize and construct a SprocketPath object using your Canvas
, Layout
, Model
, and
View
objects:
SprocketPath sPath(canvas, layout, view, model);
try {
HTML::File::Export(sPath.GetKeyPath(), exportedHTMLFilePath, htmlTemplateFilePath);
}
catch (IOException const&) {
// something went wrong with the HTML export
}
SprocketPath sPath = new SprocketPath(canvas, layout, view, model);
try
{
HTML.File.Export(sPath.GetKeyPath(), exportedHTMLFilePath, htmlTemplateFilePath);
}
catch (IOException)
{
// something went wrong with the HTML export
}
Call HPS::HTML::File::Export()
with the following parameters: the keypath of your SprocketPath
, the output file path for your HTML file, and the HTML template file into which the exported model will be embedded.
For your convenience, there are two template files in the Visualize package, both of which are based on HOOPS Communicator’s StreamCache technology: HOOPSCommunicatorTemplate.html and HOOPSCommunicatorMinimalTemplate.html. The standard template includes advanced UI features, whereas the minimal template only includes the basic functionality for viewing a model.
For simplicity, we’ll discuss how to modify the minimal HTML template below.
Modifying the HTML Template
The HTML export creates a totally self-contained HTML file for viewing a model. This means that all the markup, styling, javascript, images, and model data are contained in a single file which works out of the box in any modern browser with no internet connection required.
About the HOOPS Communicator Template File
The template file is a pre-processed HTML file with all dependencies inlined. Binary dependencies such as images and model data are base64 encoded into the file as well. All this preprocessing is done when the HOOPS Communicator package is built. Browsers can automatically base64 decode binary files such as images. Additional javascript code must be used to base64 decode the model data. This code is included in the standard template and can be leveraged by customers if needed.
Creating a Custom Template File
Aside from ensuring that the contents of hoops_web_viewer.js are included in the page, there are four required steps to create a custom template file. (To see a sample template, please see the sample HTML template below.)
Step 1: Insert the Data Replacement String
Include the replacement string in the file:
<!-- SC_MODEL_DATA -->
The system searches the template file for this string and replaces it with base64 binary encoded data. Ideally this string should be saved in a variable:
var SC_MODEL_DATA = "<!-- SC_MODEL_DATA -->";
Step 2: Add a Container div
There must be a container div in the template in which the viewer will be created:
<div id="viewerContainer" style="width:100%; height:100%;"></div>
Feel free to add your own styling.
Step 3: Decode the Base64-Encoded Data to a Uint8 Buffer
Sample code to do this is provided in the minimal template. The _base64ToUint8Array
method is a small helper function provided by the HOOPS Communicator team:
var binaryModelData = _base64ToUint8Array(SC_MODEL_DATA)
Step 4: Create and Start a WebViewer Object
Specify your binary decoded data to the web viewer:
var viewer= new Communicator.WebViewer({
containerId: "viewerContainer",
buffer: binaryModelData,
});
Look, Feel, and Functionality
Aside from the required elements enumerated above, users are free to add in their own HTML / JS / CSS code to the template. HOOPS Communicator customers may use the entirety of the Communicator API to create their own customized pages.
There are some restrictions for non-Communicator customers. The following classes aren’t available to non-Communicator customers:
Communicator.MeshData
Communicator.MeshInstanceData
The following functions aren’t available to non-Communicator customers:
Communicator.Model.createMesh
Communicator.Model.createMeshInstance
Helpful Hints
It can be challenging to work only in a single template file, especially given the size of the required javascript for the HOOPS WebViewer. There are a number of tools which can take a website and convert it to a self contained file. It would be helpful to design your template using normal separation of HTML, JS, CSS and employ a tool to combine all of it into your final template. We have had good luck using the webpage2html python module.
Sample HTML Template
The sample below shows the minimal HTML Export template; this file is available in the package in the samples/data directory. It will decode the plaintext data and start the viewer:
HTML sample file
<pre>
<html>
<head>
<title>Minimal HTML Export Template</title>
<script>
//The contents of hoops_web_viewer.js will appear here.
</script>
<script>
function _base64ToUint8Array(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
}
var SC_MODEL_DATA = "<!-- SC_MODEL_DATA -->";
var hwv = null;
var ui = null;
window.onload = function () {
hwv = new Communicator.WebViewer({
containerId: "viewerContainer",
buffer: _base64ToUint8Array(SC_MODEL_DATA),
});
window.onresize = function (event) {
hwv.resizeCanvas();
};
hwv.start();
}
</script>
<style>
body{
margin: 0;
padding 0;
}
#viewerContainer{
position:relative;
width:100%;
height:100%;
}
</style>
</head>
<body>
<div id="viewerContainer"></div>
</body>
</html>
OBJ Export
To export your scene to an OBJ file, create a HPS::SprocketPath
using your canvas, model, layout, and view. Then simply pass the SprocketPath
as a keypath to the HPS::OBJ::File::Export
function along with your file path:
SprocketPath path(canvas, canvas.GetAttachedLayout(), view, model);
UTF8 output_file = my_export_directory + "/obj_export_1.obj";
OBJ::ExportNotifier export_notifier = OBJ::File::Export(output_file, path.GetKeyPath());
SprocketPath path = new SprocketPath(canvas, canvas.GetAttachedLayout(), view, model);
string output_file = my_export_directory + "/obj_export_1.obj";
OBJ.ExportNotifier export_notifier = OBJ.File.Export(output_file, path.GetKeyPath());
In addition to an OBJ file, a file with an MTL extension will also be created in the output directory for most OBJ exports. This MTL file contains color and texture information. An image file will also be created for each texture used in the exported model.
For limitations of OBJ export, please see Supported File Formats.