Mesh Surface Object


Introduction

The Mesh Surface object is a powerful object designed to display surface data. Data can be provided as triangles. quadrilaterals or polygons. A mesh surface object can be created using function XintView3DCreateMeshSurface. Surfaces can be displayed using different front and back materials. Several convenience functions are provided to help create Mesh Surface objects for common formats, such as a uniform grid or a triangulated surface stored in an ASCII file using the GOCAD format.


Figure 5: Example with multiple surfaces


Example

The example below, extracted from subroutine XintView3DCreateUniformGrid, illustrates how to create a Mesh Surface object from a uniform grid. A grid can be mapped into a Mesh Surface of type XintOBJECT_MESH_SURFACE_QUAD_STRIP or XintOBJECT_MESH_SURFACE_QUADS. We use the former because it is faster to display.

The following code sample shows how to build the list of indices required by subroutine XintView3DCreateMeshSurface in the case where the input vertices are stored in a regular grid.

    int          width;          /* width of the grid */
    int          height;         /* height of the grid */
    XintVector3  *verts;         /* grid data (size is width*height) */
    int          num_strips;     /* Number of strips in quad mesh */
    int          **indices;      /* Index lists */
    int          *num_indices;   /* Number of indices per list */
    int          i, j, k, index;
    ...
     /*
      * Build list of indices to describe grid as quad strips
      */
     num_strips = width - 1;
     num_indices = (int *)malloc(num_strips * sizeof(int));
     indices = (int **)malloc(num_strips * sizeof(int *));
     for (i=0; i < num_strips; i++) {
          num_indices[i] = height * 2;
          indices[i] = (int *)malloc(num_indices[i] * sizeof(int));
     }
  
     /* loop over the strips */
     for (k=0; k < (width - 1); k++) {
          index = width * (k);
          i = 0;
          for (j=0; j < height; j++) {
               indices[k][i] = width + index;
               i++;
               indices[k][i] = index;
               i++;
               index++;
          }
     }

The following diagram illustrates how each list of indices is built.


Figure 6: List of indices used to specify a quad strip

Function XintView3DCreateMeshSurface allows the user to specify a normal at each vertex on the surface. If the normals are not specified, they will be generated automatically. The normal at each vertex is generated by averaging the normals of each polygon that contains the vertex. A normal is a normalized vector, which is perpendicular to the surface and which points away from the surface. Normals are required to provide proper calculation of shading in the case where the surface has lighting turned on. Two convenience functions are provided to calculate normal vectors. Function XintGeomCalculateNormal takes as input three vertices and returns the corresponding normal vector. Function XintVectorNormalize3d takes as input a vector and returns a normalized vector.

The following figure demonstrates how the normals are calculated automatically.


Step 1: Calculation of normals at the center of each quadrilateral.


Step 2: Normal at a vertex is the average of four surrounding normals.

Figure 7: Normal calculation at a grid vertex


Verifying the Calculation of Normals

A convenience function, XintView3DCreateNormalsForMesh, is provided to help the application verify that its normals are correct. This function takes a mesh surface and creates a line object containing all the normals for the mesh surface.

The following code sample, extracted from example file checknormal.c, illustrates how to use function XintView3DCreateNormalsForMesh.

    Widget             view3d;
    XintView3DMaterial *material;
    XintView3DObject   *mesh, *normals;

     ...

    /* material to display the normals */
    color.single[0] = 0.0;
    color.single[1] = 0.0;
    color.single[2] = 1.0;
    color.single[3] = 1.0;
    material = XintView3DCreateMaterial(NULL, XintMATERIAL_SINGLE_COLOR, 
                                        &color, 0, NULL);

    /* Create line object containing normals */
    normals = XintView3DCreateNormalsForMesh(mesh, 5.0);

    /* Add object to display */
    XintView3DAddObjectAndMaterial (view3d, normals, material, NULL);

    ...

The output of this example is shown below.


Figure 8: Surface and its normal vectors


4D Attributes

Mesh surface objects can easily be colored using a "4D" attribute. Creation function XintView3DCreateMeshSurface provides for a 4D argument that can be used in conjunction with material types XintMATERIAL_COLOR_SCALE. In addition, the color values for each vertex can be directly assigned into a material, when using material types XintMATERIAL_MULTI_COLOR or XintMATERIAL_COLOR_INDEX.

The following code sample, extracted from example grid4d.c, illustrates how to display grid surface and color it based on a fourth attribute. We use material type XintMATERIAL_COLOR_SCALE in this example.

    Widget                  top_level, view3d;
    XintView3DObject        *surface;
    XintView3DMaterial      *material;
    XintView3DMaterialColor scale_color;
    XintView3DColorTable    *color_table;
    XintColor4              *colors;
    XintVector3             *verts;
    int                     ncolors;
    XintReal                *range, *data, *color_data;
    float                   start_x, start_y, end_x, end_y;
    float                   min_value, max_value;
    float                   dx, dy;
    int                     nx, ny;
    int                     count;
    int                     i, j;

    ...

    /* Create a viewer */
    view3d = XtVaCreateManagedWidget("Viewer", xintView3DWidgetClass, top_level,
                                     XmNwidth,  400, XmNheight, 400, NULL);

    /* Read a colormap file */
    color_table = XintView3DReadColorTable(NULL, "contour.cmp");
    if (!color_table) 
        XtError("Error reading colormap file contour.cmp");

    /* Retrieve colors from the map */
    colors = XintView3DColorTableData(color_table, &ncolors);

    /*Read first grid used for surface */
    data = ReadGrid("topology.grd", &nx, &ny, &start_x, &end_x, 
                    &start_y, &end_y);

    /* Build list of vertices */
    verts =  (XintVector3 *)malloc(nx * ny * sizeof(XintVector3));

    dx = (end_x - start_x) / (nx - 1);
    dy = (end_y - start_y) / (ny - 1);

    count = 0;
    for (j=0; j < ny; j++) {
         for (i=0; i < nx; i++) {
              verts[count][0] = start_x + (float) i * dx;
              verts[count][1] = start_y + (float) j * dy;
              verts[count][2] = data[count];
              count++;
         }
    }

    /* Read color attribute from another grid file*/
    color_data = ReadGrid("attribute.grd", &nx, &ny, 
                          &start_x, &end_x, &start_y, &end_y);

    /* Find min and max */
    min_value = color_data[0];
    max_value = color_data[0];
    for (i=1; i < nx*ny; i++) {
         if (color_data[i] > max_value) max_value = color_data[i];
         if (color_data[i] < min_value) min_value = color_data[i];
    }

    /* Setup range */
    range = (XintReal *) XtMalloc(sizeof(XintReal) * (ncolors-1));
    for (i=0; i < ncolors-1; i++)
        range[i] = min_value + (float)i * (max_value - min_value) / (ncolors-1);

    /* Create a color scale material */
    scale_color.scale.orientation = `W';
    scale_color.scale.range = range;
    scale_color.scale.num_colors = ncolors;
    scale_color.scale.colors = colors;
    material = XintView3DCreateMaterial(NULL, XintMATERIAL_COLOR_SCALE, 
                                        &scale_color, 0, NULL);

    /* Create uniform grid surface object */
    surface = XintView3DCreateUniformGrid("grid", nx, ny, verts, 
                                          True, color_data, NULL, 
                                          False, NULL, NULL);

    /* Free memory */
    XintView3DColorTableFree(color_table);
    XtFree((char *) range);
    XtFree((char *) verts);
    XtFree((char *) data);
    XtFree((char *) color_data);

    /* Add object to display */
    XintView3DAddObject(view3d, surface, material, NULL);

    ...

The output of this example is shown below.


Figure 9: Surface colored based on a 4D attribute