Creating Stream Cache Models

Please be sure to first review the Stream Cache format. The StreamCache API is used to author your own StreamCache models, and support is provided for C++ and Java.

You can take advantage of the Stream Cache API when authoring your own models. If you are using the C++ library you will need to include sc_store.h and link to sc.lib. If you are using Java you will need to include scj.jar and corresponding shared library sc_java.dll.

Note

For more information on the location of these files in your HOOPS Communicator package, consult our Package Description page.

License

Before using any SC functions, you must initialize the SC::Store::Database with your license key. The easiest way to accomplish this is to use the HOOPS_LICENSE definition which is available by including hoops_license.h.

SC::Store::Database::SetLicense(HOOPS_LICENSE);

Models

The first thing to do is open a read/write view of a stream cache based on the path that you supply. The directory must exist beforehand; otherwise, an exception is thrown. This function takes a path and a logger as parameters. The logger must be derived from SC::Store::Logger.

Logger sLogger(true);
SC::Store::Cache cache = SC::Store::Database::Open(sLogger);

Here is a basic implementation of the logger function:

class Logger : public SC::Store::Logger {
public:
  Logger(bool sc_logging) : sc_logging(sc_logging) {};
  virtual void Message(const char * message) const {
        if (sc_logging)
          printf("SC: %s", message);
  }
  bool sc_logging;
};

Next, create and open the model, and declare some variables we’ll use later:

SC::Store::Model model = cache.Open(file_path_string.c_str());
SC::Store::InclusionKey include_key = model.Include(model);

Creating geometric data

To actually create geometric data (in this case, just a single triangle), we’ll define a mesh in our model. The SC::Store::Mesh object contains all the raw float data associated with the mesh (for example, the x-y-z coordinates of the points). Below are examples of how to create and store this data in the Mesh object:

SC::Store::Mesh mesh;
float point_data[] = {
  0.0f, 0.0f, 0.0f, // Point 0
  1.0f, 0.0f, 0.0f, // Point 1
  1.0f, 1.0f, 0.0f, // Point 2
};
mesh.point_count = 3;
mesh.points = (SC::Store::Point const *)point_data;
// (Alternatively a SC::Store::Point could be made directly instead of being cast from a float array.)

Normals:

float normal_data[] = {
  0.5f, 0.5f, 0.5f, // Point normal 0
  0.5f, 0.5f, 0.5f, // Point normal 1
  0.5f, 0.5f, 0.5f, // Point normal 2
};
mesh.normal_count = 3;
mesh.normals = (SC::Store::Normal const *)normal_data;
// (Alternatively a SC::Store::Normal could be made directly instead of being cast from a float array.)

UV parameters:

float uv_data[] = {
  0.75f, 0.75f, // UV parameter 0
  0.75f, 0.75f, // UV parameter 1
  0.75f, 0.75f, // UV parameter 2
};
mesh.uv_count = 3;
mesh.uvs = (SC::Store::UV const *)uv_data;
// (Alternatively a SC::Store::UV could be made directly instead of being cast from a float array.)

Color channels: Each channel in an RGBA32 is an unsigned byte, with values in the inclusive range [0, 255]. RGBA32 arrays are for colors local to a specific mesh. To add colors to instanced meshes, use materials.

SC::Store::RGBA32 rgba32[] = {
  SC::Store::RGBA32(0xFF, 0x00, 0x00, 0xFF), // Color 0 (Red)
  SC::Store::RGBA32(0x00, 0xFF, 0x00, 0xFF), // Color 1 (Green)
  SC::Store::RGBA32(0x00, 0x00, 0xFF, 0xFF), // Color 2 (Blue)
};
mesh.rgba32_count = 3;
mesh.rgba32s = rgba32;

Assigning parameters

In this fragment, we are assigning the points, normals, UV parameters, and colors that we defined previously to a SC::Store::MeshElement object:

// Add a single face that consists of a single triangle.
{
  mesh.face_elements.resize(1);
  SC::Store::MeshElement & face_mesh_element = mesh.face_elements[0];

  // Indices for vertex 0
  face_mesh_element.indices.push_back(0); // Point 0
  face_mesh_element.indices.push_back(0); // Normal 0
  face_mesh_element.indices.push_back(0); // UV parameter 0
  face_mesh_element.indices.push_back(0); // Color 0
  // Indices for vertex 1
  face_mesh_element.indices.push_back(1); // Point 1
  face_mesh_element.indices.push_back(1); // Normal 1
  face_mesh_element.indices.push_back(1); // UV parameter 1
  face_mesh_element.indices.push_back(1); // Color 1

  // Indices for vertex 2
  face_mesh_element.indices.push_back(2); // Point 2
  face_mesh_element.indices.push_back(2); // Normal 2
  face_mesh_element.indices.push_back(2); // UV parameter 2
  face_mesh_element.indices.push_back(2); // Color 2
}

The code fragment above defines a face and would result in a rendered triangle. If you also want the edges and points of the triangle to appear, you should define them as follows:

// Add a single polygonal line around our triangle.
mesh.polyline_elements.resize(1);
SC::Store::MeshElement & polyline_mesh_element = mesh.polyline_elements[0];
polyline_mesh_element.indices.push_back(0); // Point 0
polyline_mesh_element.indices.push_back(1); // Point 1
polyline_mesh_element.indices.push_back(2); // Point 2
polyline_mesh_element.indices.push_back(0); // Point 0

// Add in vertices for our triangle mesh.
mesh.point_elements.resize(1);
SC::Store::MeshElement & point_mesh_element = mesh.point_elements[0];
point_mesh_element.indices.push_back(0); // Point 0
point_mesh_element.indices.push_back(1); // Point 1
point_mesh_element.indices.push_back(2); // Point 2

Inserting geometry

Once all the mesh parameters have been assigned, we can add the mesh to the opened model. After insertion, the data that was used to create the cache is no longer needed and can be disposed.

SC::Store::MeshKey mesh_key = model.Insert(mesh);

After the geometry is created, it must then be instanced before it can be rendered. Geometry that has not been instanced is ignored by the Stream Cache.

Materials

Stream Cache supports materials. Here, we will create three RGB color channels. Each channel in SC::Store::Color is a float value in the inclusive range [0, 1].

float red_color_data[] = { 1.0f, 0.0f, 0.0f, 1.0f };
SC::Store::Color * red_color = (SC::Store::Color *)red_color_data;
// (Alternatively a SC::Store::Color could be made directly instead of being cast from a float array.)
float green_color_data[] = { 0.0f, 1.0f, 0.0f, 1.0f };
SC::Store::Color * green_color = (SC::Store::Color *)green_color_data;
float blue_color_data[] = { 0.0f, 0.0f, 1.0f, 1.0f };
SC::Store::Color * blue_color = (SC::Store::Color *)blue_color_data;
SC::Store::MaterialKey material_key_red = model.Insert(*red_color);
SC::Store::MaterialKey material_key_green = model.Insert(*green_color);
SC::Store::MaterialKey material_key_blue = model.Insert(*blue_color);

Matrices

Let’s define a scale matrix in our model:

float matrix_data[] = {
  5.0f, 0.0f, 0.0f,
  0.0f, 5.0f, 0.0f,
  0.0f, 0.0f, 5.0f,
  0.0f, 0.0f, 0.0f,
};
SC::Store::Matrix3d * matrix = (SC::Store::Matrix3d *)matrix_data;
// (Alternatively a SC::Store::Matrix3d could be made directly instead of being cast from a float array.)

SC::Store::MatrixKey matrix_key = model.Insert(*matrix);

Creating geometry instances

Now that we have mesh, material, and matrix definitions, we can combine these to add instanced geometry into our cache.

This general pattern of Mesh --> MeshElement --> MeshKey --> InstanceKey is the way you will create most geometry. Note that these objects are independent of each other and can be used repeatedly to create multiple geometries.

In the code sample below, multiple instances are being created, but some instances have different matrices and materials.

SC::Store::InstanceKey instance_key_1 = model.Instance(mesh_key);
SC::Store::InstanceKey instance_key_2 = model.Instance(mesh_key, matrix_key);
SC::Store::InstanceKey instance_key_3 = model.Instance(mesh_key, matrix_key, material_key_blue);
SC::Store::InstanceKey instance_key_4 = model.Instance(
  mesh_key,
  matrix_key,
  material_key_red,
  material_key_green,
  material_key_blue,
  SC::Store::MaterialMapKey(),
  SC::Store::MaterialMapKey(),
  SC::Store::MaterialMapKey(),
  SC::Store::Visibility(SC::Store::Visibility::Face));
// Set a component of an instance. Overrides the old component.
model.Set(instance_key_1, material_key_blue, material_key_green, material_key_red); //blue faces, green edge, red points
model.Set(instance_key_2, matrix_key);
model.Set(instance_key_3, mesh_key);
model.Set(instance_key_4, SC::Store::Visibility(SC::Store::Visibility::Face | SC::Store::Visibility::Line));

// Delete an instance from our model.
model.Delete(instance_key_4);

Camera

Let’s set a default camera for our model.

model.Set(SC::Store::Camera(
  SC::Store::Camera::Perspective, // Camera projection
  SC::Store::DPoint(0.0, 0.0, -5.0), // Camera position
  SC::Store::DPoint(0.0, 0.0, 0.0), // Camera target
  SC::Store::DVector(0.0, 1.0, 0.0), // Camera up vector
  2.0, 2.0)); // Camera width and height

The complete code sample is included in the package as sc_store_sample.cpp.

Authoring the assembly tree

This set of functions (available in SC::Store::AssemblyTree) allows you to author the assembly tree node structure:

SC::Store::AssemblyTree assemblyTree(sLogger);  // Creates assembly tree authoring API instance
uint32_t root_id = 0;
uint32_t child_id_1 = 0, child_id_2 = 0, child_id_3 = 0, child_id_4 = 0;
uint32_t root_child_id = 0;
if (assemblyTree.CreateAssemblyTreeRoot(root_id))    // Creates assembly tree root
{
  if (assemblyTree.CreateChild(root_id, root_child_id))  // Creates a child node linked to the root
  {
        assemblyTree.SetNodeName(root_child_id, "assembly root child");
        assemblyTree.CreateChild(root_child_id, child_id_1);  // Adds child and siblings to the node we just created
        assemblyTree.CreateChild(root_child_id, child_id_2);
        assemblyTree.CreateChild(root_child_id, child_id_3);
        assemblyTree.CreateChild(root_child_id, child_id_4);
        assemblyTree.SetNodeName(child_id_1, "child1");
        assemblyTree.SetNodeName(child_id_2, "child2");
        assemblyTree.SetNodeName(child_id_3, "child3");
        assemblyTree.SetNodeName(child_id_4, "child4");
  }
}

The local matrix, material, and visibility of a node can also be specified:

SC::Store::Matrix3d local_matrix;
assemblyTree.SetNodeLocalTransform(child_id_1, local_matrix);

SC::Store::Material material;
assemblyTree.SetNodeMaterial(child_id_1, material);

assemblyTree.SetNodeVisibility(child_id_1, true);
assemblyTree.SetNodeWasRemoved(child_id_1, false);

Or, some attributes can be added to the node:

assemblyTree.AddAttribute(root_child_id, "Author", SC::Store::AssemblyTree::AttributeTypeString, "John Smith");
assemblyTree.AddAttribute(root_child_id, "Date", SC::Store::AssemblyTree::AttributeTypeTime, "08/15/2012");
assemblyTree.AddAttribute(root_child_id, "Version", SC::Store::AssemblyTree::AttributeTypeInt, "5");
assemblyTree.AddAttribute(root_child_id, "Precision", SC::Store::AssemblyTree::AttributeTypeFloat, "0.0001");

You can then link the instance keys returned by instancing bodies with the model:

SC::Store::MeshKeys mesh_keys; //Put your mesh keys in here
for (int i = 0; i < mesh_keys.size(); i++) {
  SC::Store::InstanceKey instance_key = model.Instance(mesh_keys[i]);
  uint32_t body_instance_node = 0; // put your body instance nodes here
  assemblyTree.CreateAndAddBodyInstance(child_id_1, body_instance_node);
  assemblyTree.SetBodyInstanceMeshInstanceKey(body_instance_node, SC::Store::InstanceInc(include_key, instance_key));
}

You can then create parts, and link them to assembly tree nodes:

uint32_t part_id_1 = assemblyTree.CreatePart();
assemblyTree.SetPart(child_id_1, part_id_1);
uint32_t part_id_2 = assemblyTree.CreatePart();
assemblyTree.SetPart(child_id_2, part_id_2);
assemblyTree.SetPart(child_id_3, part_id_2);
assemblyTree.SetPart(child_id_4, part_id_2);   // Note that in this case child 2, 3, and 4 will share the same part data

On the created part, you can add bodies:

uint32_t body_id_1, body_id_2;
assemblyTree.CreateAndAddBody(part_id_2, body_id_1);
assemblyTree.CreateAndAddBody(part_id_2, body_id_2);

On bodies, you can author measurement data:

SC::Store::Point origin(0.0f, 0.0f, 0.0f);
SC::Store::Vector normal(0.0f, 1.0f, 0.0f);
assemblyTree.SetLineEdgeMeasurementData(body_id_1, 135, 6.2f);
assemblyTree.SetCircleEdgeMeasurementData(body_id_1, 98, 8.0f, origin, normal);
assemblyTree.SetCylinderFaceMeasurementData(body_id_1, 57, 5.0f, origin, normal);
assemblyTree.SetPlaneFaceMeasurementData(body_id_1, 243, origin, normal);

Then you can save everything to a model:

assemblyTree.SerializeToModel(model);

Or you can save it to an XML file:

assemblyTree.SerializeToXML(xml_filepath);

Additionally, you can load an assembly tree structure from an already existing XML file:

assemblyTree.DeserializeFromXML(xml_filepath);

The XML should look like this:

<!--HC 4.0-->
<Root>
        <ModelFile>
                <ProductOccurence Id="0" Name="2+2-Cubes" Behaviour="1" Children="1"/>
                <ProductOccurence Id="1" Name="2+2-Cubes" Behaviour="0" Children="2">
                        <Material Color="1 1 1 1"/>
                        <Transformation RelativeTransfo="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"/>
                </ProductOccurence>
                <ProductOccurence Id="2" Name="Default" Behaviour="0" Children="3 4">
                        <Transformation RelativeTransfo="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"/>
                        <Attributes>
                                <Attr Name="SW-File Name(File Name)" Type="String" Value="2+2-Cubes"/>
                                <Attr Name="SW-Folder Name(Folder Name)" Type="String" Value="E:\Tests\SolidWorks\By Issues\New Reader\Too much or to few Solids get\ShowHide\2\"/>
                                <Attr Name="SW-Short Date(Short Date)" Type="String" Value="21/10/2014"/>
                                <Attr Name="SW-Long Date(Long Date)" Type="String" Value="mardi 21 octobre 2014"/>
                                <Attr Name="SW-Configuration Name(Configuration Name)" Type="String" Value="Default"/>
                                <Attr Name="SW-Author(Author)" Type="String" Value=""/>
                                <Attr Name="SW-Keywords(Keywords)" Type="String" Value=""/>
                                <Attr Name="SW-Comments(Comments)" Type="String" Value=""/>
                                <Attr Name="SW-Title(Title)" Type="String" Value=""/>
                                <Attr Name="SW-Subject(Subject)" Type="String" Value=""/>
                                <Attr Name="SW-Created Date(Created Date)" Type="String" Value="lundi 10 décembre 2012 16:32:25"/>
                                <Attr Name="SW-Last Saved Date(Last Saved Date)" Type="String" Value="mardi 21 octobre 2014 18:21:12"/>
                                <Attr Name="SW-Last Saved By(Last Saved By)" Type="String" Value="erwan"/>
                                <Attr Name="Assembly type" Type="Int" Value="0"/>
                                <Attr Name="SW-HasDesignTable" Type="Int" Value="0"/>
                                <Attr Name="Component Type" Type="Int" Value="0"/>
                        </Attributes>
                </ProductOccurence>
                <ProductOccurence Id="3" Name="2-Cubes-1" Behaviour="1" Children="5 6">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 -130.25111389160156 197.71389770507812 -126.79463195800781 1"/>
                </ProductOccurence>
                <ProductOccurence Id="5" Name="Cube-1" Behaviour="1" InstanceRef="8">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 57.656867980957031 48.991302490234375 58.701534271240234 1"/>
                </ProductOccurence>
                <ProductOccurence Id="8" Name="" Behaviour="1">
                        <PartDefinition Id="7" FilePath="000001E6223BDFE0"/>
                </ProductOccurence>
                <ProductOccurence Id="6" Name="Cube-2" Behaviour="1" InstanceRef="8">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 239.89179992675781 94.406867980957031 79.417366027832031 1"/>
                </ProductOccurence>
                <ProductOccurence Id="4" Name="2-Cubes-2" Behaviour="0" Children="9 10">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 -131.93794250488281 90.37158203125 -251.8642578125 1"/>
                </ProductOccurence>
                <ProductOccurence Id="9" Name="Cube-1" Behaviour="1" InstanceRef="8">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 57.656867980957031 48.991302490234375 58.701534271240234 1"/>
                </ProductOccurence>
                <ProductOccurence Id="10" Name="Cube-2" Behaviour="0" InstanceRef="8">
                        <Transformation RelativeTransfo="1 0 -0 0 -0 1 0 0 0 0 1 0 239.89179992675781 94.406867980957031 79.417366027832031 1"/>
                </ProductOccurence>
        </ModelFile>
</Root>

If you’ve already built model files for each part and registered the assembly tree data, you can build the master assembly model file using one function call:

assemblyTree.BuildMasterAssemblyModel(models_path_utf8, assembly_model_path_utf8, nullptr, false);

For a more in-depth look at authoring using Stream Cache API, please see the examples in the Communicator package, located at authoring -> libsc -> examples.

Exception handling

IMPORTANT: The Stream Cache API will throw exceptions when errors are encountered. Your code should be prepared to catch SC::Store::Exception::Exception (inherits from std::exception) and handle it as gracefully as possible.