Keywords: main

Next: 4.3 Geometry Shader API Up: 4. Geometry Shaders Previous: 4.1 Introduction

# 4.2 Examples

The following example shader creates an axis-aligned unit cube at the origin. Using unit sizes and centering objects at the origin is usually a good idea because the position, orientation, and sizes of objects are better adjusted with instances than by hardcoding them into the object. This assumes that is switched to object space mode, which is recommended for all scenes anyway (this is done in the `options` statement).

         #include <shader.h>
         #include <geoshader.h>

         int geocube_version(void) {return(1);}

         #define add_vector(x, y, z) \
            v.x = x; v.y = y; v.z = z;\
            mi_api_geovector_xyz_add(&v);

         static int vertex_order[] = {
            0, 3, 5, 4,
            3, 2, 6, 5,
            2, 1, 7, 6,
            1, 0, 4, 7
         };

         miBoolean geocube(
            miTag           *result,
            miState         *state,
            miTag           *mtl)
         {
            int             i, k, ix=0;
            miObject        *obj;
            miGeoVector     v;
            miTag           mtl_tag;

            mtl_tag = *mi_eval_tag(&mtl);
            obj = mi_api_object_begin(NULL);
            obj->visible = obj->shadow = obj->trace = miTRUE;
            mi_api_basis_list_clear();
            mi_api_object_group_begin(0.0);

            add_vector(-0.5., -0.5., -0.5.);
            add_vector(-0.5.,  0.5., -0.5.);
            add_vector( 0.5.,  0.5., -0.5.);
            add_vector( 0.5., -0.5., -0.5.);

            add_vector(-0.5., -0.5.,  0.5.);
            add_vector( 0.5., -0.5.,  0.5.);
            add_vector( 0.5.,  0.5.,  0.5.);
            add_vector(-0.5.,  0.5.,  0.5.);

            for (i=0; i < 4; i++)
                    mi_api_vertex_add(i);

            for (i=4; i < 8; i++)
                    mi_api_vertex_add(i);

            for (i=0; i < 16; i++)
                    mi_api_vertex_add(vertex_order[i]);

            for (i=0; i < 6; i++) {
                    mi_api_poly_begin_tag(1, mtl_tag);
                    for (k=0; k < 4; k++)
                            mi_api_poly_index_add(ix++);
                    mi_api_poly_end();
            }
            mi_api_object_group_end();
            return(mi_geoshader_add_result(result,
                                          mi_api_object_end()));

         }

The next example creates a trimmed B-Spline free-form surface. There is one trimming curve defined which is used as a hole curve to cut a square hole out of the surface. It also shows how a texture surface can be added to a surface.

         #include <shader.h>
         #include <geoshader.h>

         int bspline_surface_version(void) { return 1; }

         miBoolean bspline_surface(
            miTag           *result,
            miState         *state,
            void            *paras)
         {
            int             i, k;
            miObject        *obj;
            miGeoScalar     knot;
            miDlist         *params, *uparams, *vparams;
            miGeoRange      range;
            miApprox        approx;
            miTag           otag;
            miVector cp[] = {
                    /* ----- 16 surface control points */
                    {0, 2, 0}, {1, 2, 0}, {2, 2, 0}, {3, 2, 0},
                    {0, 3, 0}, {1, 3, 1}, {2, 3, 1}, {3, 3, 0},
                    {0, 4, 0}, {1, 4, 1}, {2, 4, 1}, {3, 4, 0},
                    {0, 5, 0}, {1, 5, 0}, {2, 5, 0}, {3, 5, 0},
                    /* ----- 4 (2d) curve control points */
                    {0.4, 0.4, 0}, {0.6, 0.4, 0},
                    {0.6, 0.6, 0}, {0.4, 0.6, 0},
                    /* ----- 4 (3d) texture surface control points */
                    {0,0,0},{1,0,0},{0,1,0},{1,1,0}};
            miGeoScalar curve_knots[] = {0, 1, 2, 3, 4};
            miUint curve_v[] = {16, 17, 18, 19, 16}; /*closed loop*/
            miUint texsurf_v[] = {20,21,22,23};
            miGeoScalar tex_uknots[] = {0.0, 1.0};
            miGeoScalar tex_vknots[] = {0.0, 1.0};

            /* ************ create object ************ */
            obj = mi_api_object_begin(NULL);
            obj->visible = obj->shadow = obj->trace = miTRUE;

            mi_api_basis_list_clear();
            mi_api_basis_add(mi_mem_strdup("bspline_3"),
                    miFALSE, miBASIS_BSPLINE, 3, 0, 0);
            mi_api_basis_add(mi_mem_strdup("bezier_1"),
                    miFALSE, miBASIS_BEZIER, 1, 0, 0);

            mi_api_object_group_begin(0.0);

            /* control points and references */
            for(i=0; i < 24; i++)
                    mi_api_vector_xyz_add(&cp[i]);
            for(i=0; i < 24; i++)
                    mi_api_vertex_add(i);

            /* ************ create trim-curve ************ */
            mi_api_curve_begin(mi_mem_strdup("curve_0"),
                    mi_mem_strdup("bezier_1"), miFALSE);

            /* curve knot sequence */
            params = mi_api_dlist_create(miDLIST_GEOSCALAR);
            for(i=0; i < 5; i++)
                    mi_api_dlist_add(params, &curve_knots[i]);

            /* curve control point references,
               non-rational (w=1) */
            for(i=0; i < 5; i++)
                    mi_api_vertex_ref_add(curve_v[i], 1.0);

            mi_api_curve_end(params);

            /* ********** create free-form surface ********** */
            mi_api_surface_begin_tag(mi_mem_strdup("surf_0"), 0);

            /* create the uv knot vectors for the
               (bezier) surface */
            for (k=0; k < 2; k++) {
                    params = mi_api_dlist_create(
                                             miDLIST_GEOSCALAR);
                    knot = 0.0;
                    for(i=0; i < 4; i++)
                            mi_api_dlist_add(params, &knot);
                    knot = 1.0;
                    for(i=0; i < 4; i++)
                            mi_api_dlist_add(params, &knot);
                    mi_api_surface_params(k == 0 ? miU : miV,
                            mi_mem_strdup("bspline_3"),
                                       0., 1., params, miFALSE);
            }

            /* control point references, nonrational(w=1) */
            for(i=0; i < 16; i++)
                    mi_api_vertex_ref_add(i, 1.0);

            /* ************ add a texture surface *********** */
            uparams = mi_api_dlist_create(miDLIST_GEOSCALAR);
            for (i=0; i < 2; i++)
                    mi_api_dlist_add(uparams, &tex_uknots[i]);
            vparams = mi_api_dlist_create(miDLIST_GEOSCALAR);
            for (i=0; i < 2; i++)
                    mi_api_dlist_add(vparams, &tex_vknots[i]);
            mi_api_surface_texture_begin(miFALSE,/* is_volume */
                                     miFALSE,   /* is_bump */
                                     mi_mem_strdup("bezier_1"),
                                     uparams,
                                     miFALSE,   /* v_rational */
                                     mi_mem_strdup("bezier_1"),
                                     vparams,
                                     miFALSE);  /* v_rational */
            /* texture surface control point references, nonrational */
            for(i=0; i < 4; i++)
                    mi_api_vertex_ref_add(texsurf_v[i], 1.0);

            /* define one hole curve on the surface */
            range.min = 0.0;
            range.max = 4.0;
            mi_api_surface_curveseg(miTRUE,        /* newloop */
                                    miCURVE_HOLE,
                                    mi_mem_strdup("curve_0"),
                                    &range);
            mi_api_surface_end();
            /* set approximation method for the surface */
            miAPPROX_DEFAULT(approx);
            approx.method = miAPPROX_CURVATURE;
            approx.cnst[miCNST_DISTANCE] = 0.01;
            mi_api_surface_approx(mi_mem_strdup("surf_0"),
                                  &approx);
            /* set approximation method for the curve */
            miAPPROX_DEFAULT(approx);
            approx.method = miAPPROX_PARAMETRIC;
            approx.cnst[miCNST_UPARAM] = 1;
            mi_api_curve_approx(mi_mem_strdup("curve_0"),
                                &approx);
            mi_api_object_group_end();
            otag =  mi_api_object_end();
            return(mi_geoshader_add_result(result, otag);
    }

The next example accepts an object as input, and creates a new object based on the input object. The new object is identical to the old one except that only points in space are copied and all normals, texture vectors, motion, and other information is ignored. Also, it deals with groups correctly only if it does not contain multiple local spaces because it ignores the transformation matrices in the instance loop.

         #include <assert.h>
         #include "shader.h"
         #include "geoshader.h"

         int facet_version(void) {return(1);}

         static miBoolean box_to_object(
            miTag         *result,
            miBox         *box)
         {
            int           i, vs = box->vert_info.sizeof_vertex;
            miObject      *obj;
            miIndex       *vert = miBOX_GET_VERTICES(box);
            miTriangle    *tri  = miBOX_GET_PRIMITIVES(box);
            miVector      norm;

            assert(box->type == miBOX_TRIANGLES);
            obj = mi_api_object_begin(NULL);
            obj->visible = obj->shadow = obj->trace = miTRUE;
            mi_api_basis_list_clear();
            mi_api_object_group_begin(0.0);

            for (i=0; i < box->vect_info.no_points; i++)
                    mi_api_vector_xyz_add(box->vectors + i);

            for (i=0; i < box->no_vertices; i++, vert+=vs)
                    mi_api_vertex_add(*vert);

            for (i=0; i < box->no_primitives; i++, tri++) {
                    mi_api_poly_begin_tag(1, 0);
                    mi_api_poly_index_add(tri->a);
                    mi_api_poly_index_add(tri->b);
                    mi_api_poly_index_add(tri->c);
                    mi_api_poly_end();
            }
            mi_api_object_group_end();
            return(mi_geoshader_add_result(result,
                                          mi_api_object_end()));
         }

         miBoolean facet(
            miTag           *result,
            miState         *state,
            miTag           *param)
         {
            miTag           leaves, scan, boxes, next;
            miBoolean       ret;

            ret = mi_geoshader_tesselate(state, &leaves,
                                         *mi_eval_tag(param));

            for (scan=leaves; scan; scan=next) {
                    miInstance *inst = mi_db_access(scan);
                    for (boxes=inst->boxes; boxes; boxes=next) {
                            miBox *box = mi_db_access(boxes);
                            ret &= box_to_object(result, box);
                            next = box->next_box;
                            mi_db_unpin(boxes);
                    }
                    next = inst->next;
                    mi_db_unpin(scan);
            }
            mi_geoshader_tesselate_end(leaves);
            return(ret);
         }

The main facet shader first tessellates the input geometry. This builds a leaf instance list containing a sequence of instances that contain both an item field pointing to the source geometry (which may be difficult to handle because it may include many different types of complex free-form surface and polygonal geometry), and a boxes field that points to triangles. Without mi_geoshader_tesselate, the boxes field may be empty, and the shader would have to do its own traversal to find the instances.

The following nested loops consider all instances, and all boxes in each instance. There are multiple instances if the input object passed to the shader as its only parameter is an instance group or a merge group. An instance may contain multiple boxes if the object has too many vectors, vertices, or triangles to fit into a single box. For each box, a new object is generated by calling box_to_object, which is similar to the previous example except that it gets its information from a box. Every new object is appended to the result using mi_geoshader_add_result.

Finally, the shader calls mi_geoshader_tesselate_end to release the instance list, including all triangle boxes, that was created by mi_geoshader_tesselate. This is important because this list may be very large, and would introduce a large memory leak if not freed.

Next: 4.3 Geometry Shader API Up: 4. Geometry Shaders Previous: 4.1 Introduction