![[Images/tutorial.gif]](Images/tutorial.gif)
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
Code snippets to create objects with the Geometry Services that can be visualized (rendered) with Geometry's Render. The code was tested and the output is shown in this page too. The code won't create anything way cool, not even the ol' tea pot, but I hope it will serve as basis for creating more complex components.
I am not an expert in Geometry services, but I had to study it to create some visualization tools, so I cannot guarantee that I am doing things the best way. Only the basic primitives are shown here, with time I hope to be able to put information & examples about more complex primitives.
For more details about the Geometry services, check the Khoros on-line manuals.
![[bar2]](Images/rule2.gif)
This page is based on the on-line manuals for Khoros 2.2 and annotations I took while trying to figure out some details, meaning that it was written from scratch with Khoros 2.2 but I cannot guarantee that it will work with Khoros 2.1 or Khoros 2.0.2 - not to mention older versions. Since the geometry services were rewritten for K2.2, if you want to use this tutorial I'd suggest you to upgrade.
I got some annoying errors using Geometry in a FreeBSD 2.2 machine with XFree86 3.3 installed. One is that I got several "example in free(): warning: junk pointer, too low to make sense." warnings when running the examples and other is that sometimes render crashes when I try to change the point of view of the object, but as far as I know it is not a problem in the code.
I got the following errors or unexpected behaviours when trying to:
In a recent message to the Khoros Mailing list, Tom Konerth said that not all primitives are supported yet, and gave a list for the ones that are fully, partially and non-supported (I will try to get a copy and add it to these pages).
![[bar2]](Images/rule2.gif)
The output of these programs will be used to Geometry's render to visualization and output. A small workspace that can do this is shown in the next figure:
![[Images/geomtestwk.gif]](Images/geomtestwk.gif)
The "User Defined" glyph should contain the name of the output of any of the programs shown in this page. It will serve as input for the "Print Geometry" glyph, which output will be used by the glyph "File Viewer" for visualization - just to check which are the contents of the input geometry object.
The same geometry object will be used by the "Render" glyph for visualization. The output of Render will be multiplied by 255 and converted to unsigned byte by the glyph "Convert Type" (because the output of Render consist of floating point values from 0-1) and then exported to PPM by the "Supported Formats" glyph. The result can also be visualized with the "Display Image" glyph. For reasons I don't know, the displayed results looks better than the rendered results.
![[bar2]](Images/rule2.gif)
The programs shown in this page are complete and ready to run, but you need to configure your environment to compile them. The easiest way to do this is create a directory for each program (can be outside of the Khoros directory hierarchy) and save the file in that directory as example.c. After this, create the makefiles for an independent example program with the command kgenimage -tb TOOLBOX -example where TOOLBOX should be a name of a toolbox that is dependent on the dataserv toolbox. After this, an Imakefile and Makefile will be created in that directory that will allow the creation of a binary from the example.c file.
The first line in the example files in this page show an include file, this include file must match the toolbox used as a base in the kgenimake command above. For example, if you decide to create your example Imakefile based on the dataserv toolbox, the #include command must be #include "dataserv.h".
![[bar]](Images/rule.gif)
Here are the basic steps in a program that will create geometry data:
We will see examples based on this basic steps below.
![[bar]](Images/rule.gif)
In this example we will create three spheres with radii 1,3,2 and coordinates (0,0,0), (0,0,5) and (10,5,0).
The code starts HERE
#include "color.h"/* first we declare some data that will be constant for our program */
#define NSPHERES 3 /* handy way to declare all the data we need that depends on the number of spheres */
float locations[NSPHERES*3] = { 0, 0, 0, 0, 0, 5, 10, 5, 0, }; /* the data must be float, the centers of the spheres will be on those X, Y, Z coordinates */
float radii[NSPHERES] = {1,3,2}; /* the three radii for the spheres, must also be float */
main() { kgeom_object *obj; /* our geometry object */ kgeom_spheres *spheres; /* some primitives that will be on our object */ /* create a new geometry object */ obj = kgeom_new_object(); /* should check if the result is NULL ! */ obj->name = kstrdup("Spheres"); /* create a name for it */ /* create some spheres and add them to the object */ spheres = (kgeom_spheres *) kgeom_new_primitive(KGEOM_SPHERES); /* construct new primitive, should check if it is NULL */ spheres->nverts = NSPHERES; /* the number of spheres */ spheres->locs = locations; /* the locations that we defined up there */ spheres->radii = radii; /* the radii of the spheres */ kgeom_add_primitive(obj, (kgeom_primitive *)spheres); /* add the primitive to the object */ /* write it to a file */ kgeom_write_object(obj, "spheres.geom"); /* free it */ kgeom_blast_object(obj); }
The rendered output is shown in the next figure.
![[Images/geomex1.gif]](Images/geomex1.gif)
![[bar]](Images/rule.gif)
In this example we will create six different spheres with equal radii but using the three primary and three secondary colors. The spheres will be positioned in the tips of a 3D-axis imarinary object (see the rendering result)
The code starts HERE
#include "color.h"#define NSPHERES 6 /* same as above, now we want 6 spheres which will be colored with the primary and secondary colors and positioned in the tips of a 3D-axis thingie */
float locations[NSPHERES*3] = { 0, 0, +5, 0, 0, -5, 0, +5, 0, 0, -5, 0, +5, 0, 0, -5, 0, 0, };
float colors[NSPHERES*3] = { 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, };
float radii[NSPHERES] = {1,1,1,1,1,1}; /* radii will be equal */
main() { kgeom_object *obj; /* our geometry object */ kgeom_spheres *spheres; /* some primitives that will be on our object - spheres */ /* create a new geometry object */ obj = kgeom_new_object(); /* should check if the result is NULL ! */ obj->name = kstrdup("Color Spheres"); /* create a name for it */ /* create some spheres and add them to the object */ spheres = (kgeom_spheres *) kgeom_new_primitive(KGEOM_SPHERES); /* construct new primitive, should check if it is NULL */ spheres->nverts = NSPHERES; /* the number of spheres */ spheres->locs = locations; /* the locations that we defined up there */ spheres->cols = colors; /* the color for each sphere */ spheres->radii = radii; /* the radii of the spheres */ kgeom_add_primitive(obj, (kgeom_primitive *)spheres); /* add the primitive to the object */ /* write it to a file */ kgeom_write_object(obj, "spheres2.geom"); /* free it */ kgeom_blast_object(obj); }
The rendered output is shown in the next figure.
![[Images/geomex2.gif]](Images/geomex2.gif)
![[bar]](Images/rule.gif)
In this example we will use the alpha component of color specification to specify a transparency for some spheres. 30 spheres will be created, 10 for each of Red, Green and Blue colors, and they will have transparency (alpha) set from 0.1 to 1.0 in 0.1 increments. The spheres will be positioned in the X, Y, Z axis.
The code starts HERE
#include "color.h"#define NSPHERES 30 /* Now we want 30 spheres - 10 for each Red, Green, Blue color, and for these 10 the alpha value (which controls transparency) will be set from 0.1 to 1.0 The spheres will be distributed along the X,Y,Z axis */
float locations[NSPHERES*3] = { 0, 0, 2, 0, 0, 4, 0, 0, 6, 0, 0, 8, 0, 0, 10, 0, 0, 12, 0, 0, 14, 0, 0, 16, 0, 0, 18, 0, 0, 20, 0, 2, 0, 0, 4, 0, 0, 6, 0, 0, 8, 0, 0, 10, 0, 0, 12, 0, 0, 14, 0, 0, 16, 0, 0, 18, 0, 0, 20, 0, 2, 0, 0, 4, 0, 0, 6, 0, 0, 8, 0, 0, 10, 0, 0, 12, 0, 0, 14, 0, 0, 16, 0, 0, 18, 0, 0, 20, 0, 0, };
float colors[NSPHERES*4] = { 1, 0, 0, 0.1, 1, 0, 0, 0.2, 1, 0, 0, 0.3, 1, 0, 0, 0.4, 1, 0, 0, 0.5, 1, 0, 0, 0.6, 1, 0, 0, 0.7, 1, 0, 0, 0.8, 1, 0, 0, 0.9, 1, 0, 0, 1.0, 0, 1, 0, 0.1, 0, 1, 0, 0.2, 0, 1, 0, 0.3, 0, 1, 0, 0.4, 0, 1, 0, 0.5, 0, 1, 0, 0.6, 0, 1, 0, 0.7, 0, 1, 0, 0.8, 0, 1, 0, 0.9, 0, 1, 0, 1.0, 0, 0, 1, 0.1, 0, 0, 1, 0.2, 0, 0, 1, 0.3, 0, 0, 1, 0.4, 0, 0, 1, 0.5, 0, 0, 1, 0.6, 0, 0, 1, 0.7, 0, 0, 1, 0.8, 0, 0, 1, 0.9, 0, 0, 1, 1.0, }; /* Color scheme now needs 4 dimensions instead of 3 - last is the alpha */
float radii[NSPHERES] = { 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, }; /* all radii will be the same here */
main() { kgeom_object *obj; /* our geometry object */ kgeom_spheres *spheres; /* some primitives that will be on our object */ /* create a new geometry object */ obj = kgeom_new_object(); /* should check if the result is NULL ! */ obj->name = kstrdup("Lots of Spheres, some transparent"); /* create a name for it */ obj->has_alpha = TRUE; /* set its alpha to TRUE */ /* create some spheres and add them to the object */ spheres = (kgeom_spheres *) kgeom_new_primitive(KGEOM_SPHERES); /* construct new primitive, should check if it is NULL */ spheres->nverts = NSPHERES; /* the number of spheres */ spheres->locs = locations; /* the locations that we defined up there */ spheres->cols = colors; /* the colors (R,G,B,A) for each sphere */ spheres->radii = radii; /* the radii of the spheres */ kgeom_add_primitive(obj, (kgeom_primitive *)spheres); /* add the primitive to the object */ /* write it to a file */ kgeom_write_object(obj, "spheres3.geom"); /* free it */ kgeom_blast_object(obj); }
The rendered output is shown in the next figure.
![[Images/geomex3.gif]](Images/geomex3.gif)
![[bar]](Images/rule.gif)
In this example we will create a gridded cube by using disconnected lines.
The code starts HERE
#include "tutorial.h"#define GRIDSIZE 5 /* Here we will create a gridded cube with GRIDSIZE grids Grids will be created by lines that cross the cube, so there will be 3 (double) faces, GRIDSIZE^2 lines per face, 2 pointer per line and 3 coordinates per point = 3*GRIDSIZE*GRIDSIZE*3*2 coordinates for the cube. GRIDSIZE must be >= 2. */ #define NLINES 3*GRIDSIZE*GRIDSIZE #define NVERTS NLINES*2 #define NPOINTS NVERTS*3
int locindex; /* will be used for adding lines to the locations */
float locations[NPOINTS]; /* will be calculated */
/* this function will add the 3-dim, 2-point coordinates for a line in the locations vector */ void add_line(float startx,float starty,float startz,float endx,float endy,float endz) { locations[locindex] = startx; locindex++; /* add one coordinate for a vertex */ locations[locindex] = starty; locindex++; /* and increment the counter */ locations[locindex] = startz; locindex++; locations[locindex] = endx; locindex++; locations[locindex] = endy; locindex++; locations[locindex] = endz; locindex++; }
main() { kgeom_object *obj; /* our geometry object */ kgeom_polyline *dislines; /* disjoint lines */ int gridx,gridy,gridz; float startx,starty,startz,endx,endy,endz; /* print how many lines and vertices will be calculated */ kprintf("Will do %d lines (%d vertices)\n",NLINES,NVERTS); / /* set the counter for vertices to zero */ locindex = 0; /* create a new geometry object */ obj = kgeom_new_object(); /* should check if the result is NULL ! */ obj->name = kstrdup("Gridded cube"); /* create a name for it */ /* fill the location points - front to back (Z axis) */ for(gridx=0;gridx
dislines = (kgeom_polyline *) kgeom_new_primitive(KGEOM_POLYLINE_DISJOINT); /* construct new primitive, should check if it is NULL */ dislines->nverts = NVERTS; /* the number of vertices */ dislines->locs = locations; /* the location vector */ kgeom_add_primitive(obj, (kgeom_primitive *)dislines); /* add the primitive to the object */ /* write it to a file */ kgeom_write_object(obj, "cube.geom"); /* free it */ kgeom_blast_object(obj); }
The rendered output is shown in the next figure.
![[Images/geomex4.gif]](Images/geomex4.gif)
![[bar]](Images/rule.gif)
In this example we will create some text (strings) which will be positioned on the vertices of a cube, which will also be drawn.
The code starts HERE
#include "tutorial.h"/* the number of strings = the number of vertices in a cube */ #define NSTRINGS 8
/* the strings to be printed */ char *strings[NSTRINGS] = {"Black","Blue","Green","Cyan","Red","Magenta","Yellow","White"};
/* for each vertex, 3-dim coords */ float locations[NSTRINGS*3] = { 0, 0, 0, 0, 0,255, 0,255, 0, 0,255,255, 255, 0, 0, 255, 0,255, 255,255, 0, 255,255,255, };
/* the number of disconnected lines in a cube */ #define LINES 12
/* for each line, 2 vertices with 3-dim coords each */ float cube[LINES*6] = { 0, 0, 0, 0, 0,255, 0, 0, 0, 0,255, 0, 0, 0, 0,255, 0, 0, 255, 0, 0,255, 0,255, 255, 0, 0,255,255, 0, 0,255, 0, 0,255,255, 0,255, 0,255,255, 0, 0, 0,255,255, 0,255, 0, 0,255, 0,255,255, 255,255,255,255, 0,255, 255,255,255, 0,255,255, 255,255,255,255,255, 0, };
main() { kgeom_object *obj; /* our geometry object */ kgeom_polyline *dislines; /* disconnected lines for the cube lines */ kgeom_text *text; /* text primitives */ /* create a new geometry object */ obj = kgeom_new_object(); /* should check if the result is NULL ! */ obj->name = kstrdup("Annotated cube"); /* create a name for it */ /* create the disjoint lines and add them to the object */ dislines = (kgeom_polyline *) kgeom_new_primitive(KGEOM_POLYLINE_DISJOINT); /* construct new primitive, should check if it is NULL */ dislines->nverts = LINES*2; /* the number of vertices, 2 for each line */ dislines->locs = cube; /* the location vector */ kgeom_add_primitive(obj, (kgeom_primitive *)dislines); /* add the primitive to the object */ /* create the text primitive and add them to the object */ text = (kgeom_text *) kgeom_new_primitive(KGEOM_TEXT); /* construct new primitive, should check if it is NULL */ text->nverts = NSTRINGS; /* number of vertices for the strings */ text->strings = strings; /* the strings to be printed */ text->locs = locations; /* position for the strings */ kgeom_add_primitive(obj, (kgeom_primitive *)text); /* add the primitive to the object */ kgeom_write_object(obj, "acube.geom"); /* write it to a file */ kgeom_blast_object(obj); /* free it */ }
The rendered output is shown in the next figure.
![[Images/geomex5.gif]](Images/geomex5.gif)
![[bar]](Images/rule.gif)
I will try to add examples on how to display triangles, rectangles, etc. if time allows, but with the information that is here already and by reading the Khoros on-line manuals you could be able to try more complicate shapes too.
![[bar]](Images/rule.gif)
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
![[bar2]](Images/rule2.gif)
![[bar2]](Images/rule2.gif)