The ndt ray-tracer uses a modular object definition model. Each object type is defined in a single C file that defines several functions that will be called as needed by the ray-tracing engine.
The functions described below are required for each object.
int type_name(char *name, int size);
The type_name
function provides the name that will be used in
scene_alloc_object
of the C API or the type
field of an object when using a YAML file.
Parameters:
Returns:
int params(object *obj, int *n_pos, int *n_dir, int *n_size, int *n_flags, int *n_obj);
The params
function provides the number of each type of data is required to
describe the location and shape of an object.
For objects that require a variable number of parameters depending on the
number of dimensions, the dimensions field of the passed in object can be
checked.
Parameters:
Returns:
int bounding_points(struct gen_object * obj, bounds_list *list);
The bounding_points
function finds a set of points such that any sphere that surrounds all of the points will also completely surround the object.
Points are added to the bounding set using the bounds_list_add
function.
To improve performance, each object is surrounded by a bounding sphere that is
checked for intersection with a ray before checking to see if the actual
object intersects a ray.
Thus a tighter bound gives fewer false positives and therefore better
performance.
Each point can be padded with a radius around it that will also be included in the final bounding sphere.
For infinitely large objects (e.g., hyperplanes), the passed in list should
remain empty.
Parameters:
Returns:
int intersect(object * obj, vectNd *o, vectNd *v, vectNd *res, vectNd *normal, object **obj_ptr);
The intersect
function checks to see if the passed in object (obj
) and a ray
(starting from o and moving along v) intersect.
If an intersection occurs, res
is to be set to the point of the
intersections, and normal
is to be set to the normal vector at that point.
Parameters:
Returns:
int cleanup(object * obj);
The cleanup
function frees any additional persistent memory or vectors that
an object may have allocated for ray-invarient values that were pre-computed
beyond the prepped_t structure.
This function will only be called when an object is freed.
Note: Only required for objects that allocate additional memory or vectors outside the lists discussed in *params functions.*
Parameters:
Returns:
The functions below are optional. If they are omitted, a default function will be provided.
Each of these functions are passed an object (obj
) and a location vector
(at
). They then
fill in the color, reflectivity, or transparency at that location for that
object.
int get_color(object *obj, vectNd *at, double *red, double *green, double *blue);
int get_reflect(object *obj, vectNd *at, double *red_r, double *green_r, double *blue_r);
int get_trans(object *obj, vectNd *at, int *transparent);
To add a new object type, copy stubs.c
to a new filename (e.g., custom.c
).
Be sure to change the name in type_name
from stubs
to something
meaningful.
Add code as needed to the four required functions.
Any values that need to be computed during intersection checking, but don’t
vary with the ray being checked can be pre-computed in the prepare function
and stored in the prepped_t
structure.
Once the object has been defined, the C code must be compiled into a shared
object that can be loaded at runtime.
The Makefile
in the objects subdirectory should automatically build any C
files into shared objects, and ndt
will load any suitable shared objects
in the objects
subdirectory at runtime.
To build the shared object files:
$ cd objects
$ make
It should now be possible to refer to the new object by its type name in either
scene_alloc_object
calls or the type
field of objects in a YAML file.