Application Development
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).
|
1. Introduction
A suite of utilities capable of interactively displaying, editing, and interrogating geometric models is known as BRL-CAD. BRL-CAD has become a powerful constructive solid geometry (CSG) modeling package. The package includes a large collection of tools and utilities including an interactive geometry editor, ray tracing and generic framebuffer libraries, a network-distributed image-processing and signal-processing capability, and an embedded scripting language.
A particular strength of the package lies in its ability to build and analyze realistic models of complex objects using a relatively small set of "primitive shapes." Another strength of the package is the speed of its ray tracer, which is one of the fastest in existence. Finally, BRL-CAD users can accurately model objects on scales ranging from the subatomic through the galactic and get "all the details, all the time." BRL-CAD has been used for a wide variety of engineering and graphics applications. BRL-CAD is basically a collection of libraries, tools, and utilities that work together to create, raytrace, and interrogate geometry and manipulate files and data. In BRL-CAD library, Each library fits into one of three categories:
-
creating and/or editing geometry,
-
raytracing geometry
-
image handling.
The application side of BRL-CAD also offers a number of tools and utilities.
rt
-
the main raytracer for rendering images in BRL-CAD.
nirt
-
a package for firing rays interactively and getting information about what the rays run into.
remrt
-
a network-distributed ray tracing package.
2. Overview
-
Header files
-
Shooting Rays
-
Ray-Tracing User Interface Framework (RTUIF)
-
Geometry Forms
-
Creating Geometry
-
Reading Geometry
-
Modifying Geometry
3. Header Files
Header | Library |
---|---|
bu.h |
libbu |
bn.h |
libbn |
raytrace.h |
librt |
rtgeom.h |
librt / libwdb |
wdb.h |
libwdb |
vmath.h |
(data types) |
4. Prototype Application: rtexample.c
-
Opens a database
-
Retrieves geometry
-
Prepares geometry for raytrace
-
Performs raytrace
-
See source tree:
rt/rtexample.c
5. 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
-
6. 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
7. 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
8. 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
9. 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.
10. 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 *
/* ... */
};/
11. 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.
12. 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();
}
13. 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
14. 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
.
15. 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 bylibrt
.
16. 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();
}
17. 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
19. 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.
20. 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.
21. 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.
23. 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
-
-
24. 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
-
-
25. 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
26. 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
-
27. 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"
28. 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 */
}
29. 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;
30. 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]);
/* ... */
}
31. Primitive Methods
-
Retrieved geometry has a specific set of defined operations and methods available.
-
See
h/raytrace.h
for a description ofstruct rt_functab
. -
Primitives should implement every method, but some do not. See librt/table.c for specifics.
32. Putting Geometry Back
-
Database I/O layer converts from internal to external format.
wdb_export(wdbp, "mybox", arb, ID_ARB8, mk_conv2mm);
33. 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
-
34. 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);
35. 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 */
36. 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);
37. 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);
38. 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));
39. 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.
40. 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);
}
41. Making Temporary Changes
-
Changes that only last for 1 application run
-
Changes do not reside in on-disk database
42. 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
43. 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);
}
}