# Application Development

# with

# BRL-CAD

Lee A. Butler
John Anderson

## WARNING

### This a code-intensive presentation

### for Code Warriors only!

Non-programmers will need atropine, caffeine, and electro-shock therapy (run, do not walk, to the nearest exit).

## Overview

- Header files

- Shooting Rays

- Ray-Tracing User Interface Framework (RTUIF)

- Geometry Forms

- Creating Geometry

- Reading Geometry

- Modifying Geometry

## Header Files

- The Big Six

Header | Library
--- | ---
bu.h | libbu
bn.h | libbn
raytrace.h | librt
rtgeom.h | librt / libwdb
wdb.h | libwdb
vmath.h | (data types)

## Prototype Application: rtexample.c

- Opens a database

- Retrieves geometry

- Prepares geometry for raytrace

- Performs raytrace

- See source tree: `rt/rtexample.c`

## Necessary Headers

    #include "conf.h"       /* compilation macros           */
    #include <stdio.h>
    #include <math.h>
    #include "machine.h"    /* machine specific definitions */
    #include "vmath.h"      /* vector math macros           */
    #include "raytrace.h"   /* librt interface definitions  */

- The conf.h and machine.h are ubiquitous in almost all BRLCAD apps

- The raytrace.h is present for geometry programs

  - Includes some additional headers

  - Contains most ray-tracing data structure definitions

## Opening the Database

    static struct rt_i *rtip; /* librt Instance structure */
    /* rt_dirbuild() performs many functions for us */
    rtip = rt_dirbuild(argv[1], buf, sizeof(buf));
    if (rtip == RTI_NULL) {
        fprintf(stderr,"rtexample: rt_dirbuild failure\n");
        exit(2);
    }

- Opens database file

- Builds a directory of objects in the database

- Allows us to retrieve individual objects

## Reading Geometry

    if (rt_gettree(rtip, argv[2]) < 0)
        fprintf(stderr,"rt_gettree(%s) FAILED\n", argv[2]);

- Retrieves tree top specified by argv[2] into a working set used by librt

## Preparing Geometry for Raytracing

    rt_prep_parallel(rtip, 1);

- Pre-computes useful terms for each primitive,

  e.g.,triangle normals, function roots, trig terms.

- Builds space partition tree to accelerate ray-trace

## Application Struct and Shot

    struct application ap;
    ap.a_rt_i = rtip;
    VSET(ap.a_ray.r_pt, 0, 0, 10000);
    VSET(ap.a_ray.r_dir, 0, 0, -1);
    ap.a_hit = hit;	          /* where to go on a hit */
    ap.a_miss = miss;         /* where to go on a miss */

    (void)rt_shootray(&); /* do it */

- The application struct contains information about the ray that is to be computed and what should be done with the results.

## Application Struct

- Excerpts of application struct from raytrace.h:

      struct application {

          struct xray  a_ray;	/* Actual ray to be shot */
          int          (*a_hit)(struct application *,
                                struct partition *,
                                struct seg *);
          int          (*a_miss) (struct application *);
          int          a_onehit; /* flag to stop on first hit */

          struct rt_i  *a_rt_i;  /* this librt instance *
          /* ... */
      };/

## Miss Routine

    int
    miss(register struct application *ap)
    {
        bu_log("missed\n");
        return (0); /* Value returned by rt_shootray() */
    }

- Called when ray does not hit any geometry.

## Hit Routine

    int
    hit(register struct application *ap, /* see raytrace.h */
        struct partition *PartHeadp)     /* see raytrace.h */
    {
        register struct partition *pp;
        register struct hit *hitp;
        point_t pt;
        for (pp = PartHeadp->pt_forw;
             pp != PartHeadp;
             pp = pp->pt_forw ) {
            hitp = pp->pt_inhit;
            VJOIN1( pt, ap->a_ray.r_pt, hitp->hit_dist, ap->a_ray.r_dir);
            VPRINT(Hit Point, pt);
        }
        return 1; /* value returned by rt_shootray();
    }

## Hit Routine Breakdown

    int
    hit(register struct application *ap,
        struct partition *PartHeadp)
    {
        register struct partition *pp;
        register struct hit *hitp;
        point_t         pt;
        /* ... */
    }

- Partition Structure contains information about intervals of the ray which pass through geometry

- Hit structure contains information about an individual boundary/ray intersection

## Partition Structure

    struct partition {
        long              pt_magic;       /* sanity check               */
        struct partition *pt_forw;        /* forwards link              */
        struct partition *pt_back;        /* backwards link             */
        struct seg       *pt_inseg;       /* IN seg ptr (gives stp)     */
        struct hit       *pt_inhit;       /* IN hit pointer             */
        struct seg       *pt_outseg;      /* OUT seg pointer            */
        struct hit       *pt_outhit;      /* OUT hit ptr                */
        struct region    *pt_regionp;     /* ptr to containing region   */
        char              pt_inflip;      /* flip inhit->hit_normal     */
        char              pt_outflip;     /* flip outhit->hit_normal    */
        struct region   **pt_overlap_reg; /* NULL-terminated array of
                                           * overlapping regions.
                                           * NULL if no overlap.
                                           */
        struct bu_ptbl  pt_seglist;       /* all segs in this partition */
    };

- From `h/raytrace.h`.

## Hit Structure

    struct hit {
        long         hit_magic;
        fastf_t      hit_dist;    /* dist from r_pt to hit_point      */
        point_t      hit_point;   /* Intersection point               */
        vect_t       hit_normal;  /* Surface Normal at hit_point      */
        vect_t       hit_vpriv;   /* PRIVATE vector for xxx_*()       */
        void        *hit_private; /* PRIVATE handle for xxx_shot()    */
        int          hit_surfno;  /* solid-specific surface indicator */
        struct xray *hit_rayp;    /* pointer to defining ray          */
    };

- From `raytrace.h`.

- Holds information about a single ray/surface intersection.

- (Note: Only `hit_dist` is filled in by `librt`.

## Hit Routine (Again)

    int
    hit(register struct application *ap, /* see raytrace.h */
        struct partition *PartHeadp)     /* see raytrace.h */
    {
        register struct partition *pp;
        register struct hit *hitp;
        point_t pt;
        for (pp = PartHeadp->pt_forw;
             pp != PartHeadp;
             pp = pp->pt_forw ) {
            hitp = pp->pt_inhit;
            VJOIN1(pt, ap->a_ray.r_pt, hitp->hit_dist, ap->a_ray.r_dir);
            VPRINT(Hit Point, pt);
        }
        return 1; /* value returned by rt_shootray();
    }

## Using the RTUIF

- Makes shooting grids of rays easy.

- Uses the same command line interface as rt.

- Foundation for: rt, rtweight, rthide, and other raytracing based applications.

- Simplest example shown in rt/viewdummy.c in source tree

## The 5 RTUIF Functions

- view_init

- view_setup

- view_2init

- view_pixel

- view_end

## RTUIF Routines

    int view_init(struct application *ap, char *file,
                  char *obj, int minus_o);

Called by main() at the start of a run. Returns 1 if framebuffer should be opened, else 0.

    void view_setup(struct rt_i *rtip);

Called by do_prep(), just before rt_prep() is called, in do.c. This allows the lighting model to get set up for this frame, e.g., generate lights, associate materials routines, etc.

    void view_2init(struct application *ap);

Called at the beginning of a frame. Called by do_frame() just before raytracing starts.

## RTUIF Routines 2

    int rayhit(struct application *ap, struct partition *PartHeadp);

Called via a_hit linkage from rt_shootray() when ray hits.

    int raymiss(struct application *ap);

Called via a_miss linkage from rt_shootray() when ray misses.

## RTUIF 3 Routines 3

    void view_pixel(struct application *ap);

Called by worker() after the end of processing for each pixel.

    void view_end(struct application *ap);

Called in do_frame() at the end of a frame, just after raytracing completes.

## Thinking About Geometry

- How to create it

- How to read it

- Doing something useful with it

## Geometric Representation

- BRL-CAD geometry has 3 forms:

  - External (Disk/DB)

    - Space efficient

    - Network integers (Big-Endian)

    - IEEE double-precision floating point (Big-Endian)

  - Internal (Editing)

    - Convenient parameter editing

    - Host float/int representation

  - Prepped (Raytrace)

    - Fast ray/primitive intersections

## On-Disk Representation

- Space Efficient

- Machine independent

  - Only in new database format

- Database access is separate from object retrieval.

  - Database layer returns named objects.

    - Does not understand content.

  - Primitive objects get Bag-o-Bytes to turn into in-memory representation.

    - Have no knowledge of data origins

## Internal Representation

- Convenient editing form

  - Host format floating point and integers

- Must be exported to be written to disk

- Primitive shape data structures defined in h/rtgeom.h

- Combination (and hence region) structure defined in raytrace.h

## Prepped Representation

- The form that is actually raytraced

- Created from internal form by rt_prep() call

- May not include internal form

  - Saves memory

- May include additional fields

  - Pre-computed values, additional data

## Simple Database Application

- Necessary headers

    #include "conf.h"
    #include <stdio.h>
    #include "machine.h"
    #include "vmath.h"
    #include "raytrace.h"
    #include "rtgeom.h"
    #include "wdb.h"

## Opening The Database

    struct rt_wdb *wdbp;
    struct db_i *dbip = DBI_NULL;

    /* open first, to avoid clobbering existing databases */
    if ((dbip = db_open(argv[1], "r+w")) != DBI_NULL) {
        /* build a wdbp structure for convenient read/write */
        wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DISK);

        if (db_dirbuild(dbip) < 0 ) {
            /* create directory database contents */
            bu_log("Error building directory for %s\n",
                   argv[1]);
            exit(-1);
        }
    } else {
        /* it doesn't exist, so we create one */
        bu_log("doing wdb_fopen()\n");
        wdbp = wdb_fopen(argv[1]); /* force create */
    }

## Creating Geometry

- Note: All db units are in mm

  - Set mk_conv2mm global for other units

    point_t lo, hi;
    /* ... */
    /* add an axis-aligned ARB8 */
    VSETALL(lo, 0.0);
    VSETALL(hi, 2.0);
    if (mk_rpp(wdbp, "mybox", lo, hi)) /* see libwdb for APIs */
        return -1;

    /* add a sphere (really ellipse special case) */
    if (mk_sph(wdbp, "myball", hi, 0.5)) /* see libwdb for APIs */
        return -1;

## Getting Geometry

- To retrieve geometry, we have to get an internal representation.

    struct rt_db_internal ip;
    /* ... */
    RT_INIT_DB_INTERNAL(&ip);
    cond = rt_db_lookup_internal(wdbp->dbip, "mybox", &dp, &ip,
                                 LOOKUP_QUIET, &rt_uniresource);
    if (!cond) {
        bu_log("couldn't find %s\n", "mybox");
        exit(0);
    }
    if (ip.idb_major_type == DB5_MAJORTYPE_BRLCAD /* see db5.h */
        && ip.idb_minor_type == ID_ARB8 /* see raytrace.h */) {

        struct rt_arb_internal *arb; /* see rtgeom.h */
        arb = (struct rt_arb_internal *)ip.idb_ptr;
        RT_ARB_CK_MAGIC(arb);
        VPRINT("First Point", arb->pt[0]);
        /* ... */
    }

## Primitive Methods

- Retrieved geometry has a specific set of defined operations and methods available.

- See `h/raytrace.h` for a description of `struct rt_functab`.

- Primitives should implement every method, but some do not. See librt/table.c for specifics.

## Putting Geometry Back

- Database I/O layer converts from internal to external format.

    wdb_export(wdbp, "mybox", arb, ID_ARB8, mk_conv2mm);

## Building Boolean Trees

- Regions/combinations used to store boolean trees.

  - Both are same type of database record

  - old GIFT form detailed here

- Simple boolean tree that contains

  - Names of objects

  - Boolean operations.

  - Matrix transformations

- Database record contains no actual geometry.

- Example code taken from

  - libwdb/wdb_example.c

## Constructing Boolean List

Build the list of elements first:

    struct wmember wm_hd; /* defined in wdb.h */
    BU_LIST_INIT(&wm_hd.l);

    /* see h/wdb.h or libwdb/reg.c for API conv or proc-db
     * for examples
     */
    (void)mk_addmember("mybox", &wm_hd.l, NULL, WMOP_UNION);

    /* If we wanted a transformation matrix for this element, we could
     * have passed the matrix in to mk_addmember as an argument or we
     * could add the following code:
     */

    memcpy(wm_hd->wm_mat, trans_matrix, sizeof(mat_t));
    /* Remember that values in the database are stored in millimeters,
     * so the values in the matrix must take this into account.
     */
    (void)mk_addmember("myball", & wm_hd.l, NULL, WMOP_SUBTRACT);

## Regions/Combinations

- Constructing the actual combination record

  - Note: use mk_lcomb/mk_comb for initial creation only!

    - caveat: can use to update boolean tree under special conditions

    int is_region = 1;
    VSET(rgb, 64, 180, 96); /* a nice green */

    /* mk_lcomb is a macro using mk_comb.
     * See libwdb/mk_comb() for full form
     */
    mk_lcomb(wdbp,
             "box_n_ball.r", /* Name of the db element created          */
             &wm_hd,         /* list of elements and boolean operations */
             is_region,      /* Flag:  This is a region                 */
             "plastic",      /* optical shader                          */
             "di=.8 sp=.2",  /* shader parameters                       */
             rgb,            /* item color                              */
             0);             /* inherit (override) flag                 */

## Retrieving A Combination

- Simple retrieval only gets:

  - List of elements

  - Boolean operations

  - Matrix transformations.

    struct rt_comb_internal *comb; /* see raytrace.h */
    /* ... */
    rt_db_lookup_internal(wdbp->dbip, "box_n_ball.r", &dp, &ip,
                          LOOKUP_QUIET, &rt_uniresource);

    if (ip.idb_major_type != DB5_MAJORTYPE_BRLCAD /* see db5.h */
        || ip.idb_minor_type != ID_COMBINATION /* see raytrace.h */ ) {
        bu_bomb("gack\n");
    }
    comb = (struct rt_comb_internal *)ip.idb_ptr;
    RT_CK_COMB(comb);

## Combination Write-Back

- Modify the boolean tree

- Write back out to db

    /* Modify the combination we retrieved */
    BU_GET(a, union tree);
    RT_TREE_INIT(a);
    BU_GET(b, union tree);
    RT_TREE_INIT(b);

    a->tr_l.tl_name = bu_strdup("newball");
    a->tr_l.tl_op = OP_DB_LEAF;
    a->tr_l.tl_mat = (matp_t)NULL;
    a->tr_l.magic = RT_TREE_MAGIC;

    b->tr_b.magic = RT_TREE_MAGIC;
    b->tr_b.tb_left = comb->tree;
    b->tr_b.tb_right = a;
    b->tr_b.tb_op = OP_UNION;

    comb->tree = b;
    wdb_export(wdbp, "box_n_ball.r", comb, ID_COMBINATION, 1.0);

## Combination Tree Info

- Need to prep the tree to obtain geometry

  - First, create rt instance struct rt_i object

    struct rt_i *rtip; /* see raytrace.h */

    /* if we've been doing db I/O */
    rtip = rt_new_rti(wdbp->dbip);

    /* if not already doing db I/O */
    rtip=rt_dirbuild(filename, idbuf, sizeof(idbuf));

## Processing combination tree

- Now to retrieve a treetop and prep:

    rt_gettree(rtip, "box_n_ball.r");
    rt_prep(rtip);   /* now rtip has valid information */

- This could have been any level in the tree, not just a region.

## Accessing Prepped Regions

- rtip has list of regions

- Access as a linked list

- Example: getting bounding box of regions

    struct region *rp; /* see raytrace.h */

    for (BU_LIST_FOR(rp, region, &rtip->HeadRegion)) {
        point_t tree_min, tree_max;
        VSETALL(tree_max, MAX_FASTF);
        VREVERSE(tree_min, tree_max);
        if (rt_bound_tree(rp->reg_treetop, tree_min, tree_max)) {
        	bu_bomb("choke\n");
        }
        VPRINT("tree_min", tree_min); /* VPRINT is a macro from vmath.h */
        VPRINT("tree_max", tree_max);
    }

## Making Temporary Changes

- Changes that only last for 1 application run

- Changes do not reside in on-disk database

## Dynamic Geometry

- Involves special inmem database

  - Contains only modifications

  - Akin to union filesystem of Unix

- Directory structure tracks whether current version of object is on disk or in inmem database

- Object retrieval gets most current version

- Writes to inmem arranged though special wdb_dbopen() call

## Accessing inmem database

- small difference in wdb_dbopen call

- all writes to this rt_wdb will go to memory database only

    struct rt_wdb *wdb_memp;
    struct db_i *dbip = DBI_NULL;

    if ((dbip = db_open(argv[1], "r+w")) != DBI_NULL) {
        /*  The "INMEM" specifies that changes are to be made
         * ONLY in memory.  Reads still come from disk for non-mem obj
         */
        wdb_memp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);

        if( db_dirbuild( dbip ) < 0 ) { /* create database content directory */
            bu_log( "Error building directory for %s\n", argv[1] ); exit(-1);
        }
    }

## Closing the Database

- Important to flush data and purge data structures!

    wdb_close(wdbp);