NURBS Surfaces

A NURBS surface is a parametric HOOPS primitive, i.e., it’s geometric properties are determined by a set of mathematical functions. For drawing purposes, however, HOOPS calculates a polyhedron to represent the NURBS surface. It will be a mesh if untrimmed, and a shell if trimmed.


A NURBS surface with a control mesh.

A detailed description of NURBS surfaces is beyond the scope of this document. We recommend “The NURBS Book” by Piegl and Tiller as a reference on the topic.

NURBS Surface Example

The following snippet inserts a NURBS surface:

        float pts[5][5][3] = {
            {{3.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {0.0f, 2.0f, 0.0f}, {-2.0f, 2.0f, 0.0f}, {-2.0f, 3.0f, 0.0f}},
            {{3.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 2.0f, 1.0f}, {-2.0f, 2.0f, 1.0f}, {-2.0f, 3.0f, 1.0f}},
            {{3.0f, -1.0f, 2.0f}, {1.0f, -1.0f, 2.0f}, {0.0f, 0.0f, 2.0f}, {-2.0f, 0.0f, 2.0f}, {-2.0f, 1.0f, 2.0f}},
            {{3.0f, -1.0f, 3.0f}, {1.0f, -1.0f, 3.0f}, {0.0f, 0.0f, 3.0f}, {-2.0f, 0.0f, 3.0f}, {-2.0f, 1.0f, 3.0f}},
            {{3.0f, 0.0f, 4.0f}, {1.0f, 0.0f, 4.0f}, {0.0f, -1.0f, 4.0f}, {-2.0f, -1.0f, 4.0f}, {-2.0f, 2.0f, 4.0f}}};
        float weights[] = {1, 1, 1, 1, 1, 1, 0.5f, 0.5f, 0.5f, 1, 1, 0.5f, 0.5f, 0.5f, 1, 1, 0.5f, 0.5f, 0.5f, 1, 1, 1, 1, 1, 1};

        float u_knots[] = {0, 0, 0, 1, 2, 3, 4, 4, 4};
        float v_knots[] = {0, 0, 0, 1, 2, 3, 4, 4, 4};

        HC_Insert_NURBS_Surface(3, 3, 5, 5, pts, weights, u_knots, v_knots);


Associated with a NURBS Surface can be any number of trim objects. These come in the form of closed loops that “trim” away portions of the Surface. Each object is classified as either a “keep” or “remove” operation. Trim objects are initially “remove” operations until otherwise specified. Trim objects can be vertices and/or control points are specified in two-dimensional u-v parametric space normalized from 0 to 1. Trims must be closed loops. If the last point does not meet the first, a line will be artificially added to join them. Portions of the surface that have been trimmed away are never selectable. There are three types of trim objects.

  • Trim_Poly: each vertex is explicitly specified, as in Insert_Polygon or Insert_Polyline.

  • Trim_Curve: the trimming object is itself a NURBS curve. The NURBS curve is tessellated to a resolution specified in the “nurbs surface” suboption of rendering options. There is no support for a start_u and end_u parameter for trimming NURBS curves as there is for standard standalone NURBS curves.

  • Trim_Collection: a set of Trim_Poly and Trim_Curve objects that together form a single piecewise loop. This is currently the only type of geometry that can be opened with a call to Open_Trim.

../../_images/02152a_nurbspolytrim.png ../../_images/02152a_nurbscurvetrim.png ../../_images/02152a_nurbsmultitrim.png

A NURBS surface trimmed in different ways from left to right: a polygonal trim, a curve trim and multiple trims.

Unlike the contents of a segment, the collection of trims associated with a NURBS Surface are addressed according to the order in which they were inserted. The trim object set should be thought of as a simple linked list, with new objects are prepended onto the head. The last trim created will have index 0; the second to last will have index 1, etc… Trim collections have an identifier for the collection, and a completely separate list inside, accessible via Open_Trim.

The following code will cut away a large hole from the center of the surface, leaving only a small border around the perimeter.

        float pts[5][2] = {{0.1f, 0.1f}, {0.9f, 0.1f}, {0.9f, 0.9f}, {0.1f, 0.9f}, {0.1f, 0.1f}};

        HC_KEY nurbs_surface_key = HC_Insert_NURBS_Surface(3, 3, 5, 5, pts, weights, u_knots, v_knots);
        HC_Trim_NURBS_Surface_By_Poly(2, pts[0]);
        HC_Trim_NURBS_Surface_By_Poly(2, pts[1]);
        HC_Trim_NURBS_Surface_By_Poly(2, pts[2]);
        HC_Trim_NURBS_Surface_By_Poly(2, pts[3]);

After the last Trim_NURBS_Surface_By_Poly, the poly associated with pts[3] will be index 0. Notice that the trim object itself in the above code would not be correct were it not that trim objects that are part of a collection are reversed before processing. Thus, the last point of the object inserted first is taken to match with the first point of whatever is inserted second.

Tessellation Control for NURBS Surfaces

Tessellation options are set via the “NURBS surface” suboption within Set_Rendering_Options, and must be set prior to the first update.

Budget and Trim Budget

The “budget” setting specifies the base number of vertices in the untrimmed surface. There is no way to set an exact budget for a trimmed NURBS surface. The untrimmed surface is created to the specified budget, and trimming will add a small number of points on top of that. Default resolutions for NURBS surfaces are:

    HC_Set_Rendering_Options("nurbs surface=( budget=400, trim budget=20 )");


A variety of options are supported which allow the user to control the amount of deviation from the surface, including “maximum facet deviation”, “maximum facet angle”, “maximum facet width”, “maximum trim curve deviation”. Refer to “nurbs surface” option of Set_Rendering_Options for more information.

Changes to the “nurbs surface” rendering options after the first update do not by themselves force a re-tessellation. If such a re-tessellation is needed, it could be forced by inserting and then removing a trivial trim object. For example, the following code will trigger a re-tessellation.


Trim budget applies to trim curves only, not to trim poly objects. Even trim curves, though, ignore the trim budget when given a degree of 1. In that case, they behave exactly like a trim poly, with a budget exactly equal to the number of control points (whether higher or lower than the specified budget).

Accessing Facets

The underlying facets that represent a NURBS surface can be accessed. This is achieved by opening the surface and calling the Show_Shell function which returns points and face list arrays:

        HC_Show_Shell_Size(...); /* need to figure out the size of arrays to allocate */
        HC_Show_Shell(...); /* get the points and faces */


Self-intersecting trimming loops are not allowed. There is no provision for editing trim objects other than removal and reinsertion. Trim collections may not contain trim collections.

We currently do not support a knot multiplicity greater than the degree of the surface, which would cause the surface to be completely disjoint at that point. NURBS surfaces are only defined when Knot vectors are non-decreasing, and so are not supported by HOOPS. Negative control point weights are not supported. Weights, must not all be zero. In other words, all of the limitations described at the end of our section on NURBS curves also apply to NURBS surfaces, plus the restriction that weights must be non-negative.

There is currently no mechanism for view-dependent tessellation of NURBS surfaces. The time required to re-tessellate would make real-time interaction difficult for even modestly-sized models.

The polyhedron representation may not be opened, so the individual faces, edges and markers can have no explicitly set local attributes.

It is not currently possible to set a “draw 3d nurbs surface” callback.