Introduction
The probing API
The probingUI API
Required libraries and include files
Logs & file formats
First of all, you should read the article GEOPROVE: Geometric Probes for Virtual Environments and the user manual to understand what GEOPROVE is about.
The probing API is split up into two parts: the bare probing API and the probingUI API which manages a user interface. Although it is possible to code you own user interface around the bare probing API (with the calls discussed in this manual), the probingUI user interface will usually be sufficient. When you use the probingUI API, you still have to issue some calls to the probing library (e.g. to register callbacks, set options). The sample application demonstrates the use of the probingUI and probing libaries.
All probing library calls start with cp which stands for Cave Probing. All probingUI library calls start with cpui which stands for Cave Probing User Interface.
Probing API introduction
Sample code
Basic functions
Interface functions
Measurement functions
Calibration functions
Using the probing library is quite simple: there are some basic functions which initialize, update, draw and terminate the library, functions to retrieve measurements and snapshot filenames, and functions to retrieve the choices of the current interface and activate a choice. The basic functions are demonstrated in the sample code. Sample code for most of the other function can be found in probingUI.c, the file which implements the probingUI library.
The most basic application using the probing API (without error checking) would look something like:
#include <cave_ogl.h>
#include <probing.h>
int main(int argc, char *argv[]) {
CAVEConfigure(...); // configure the CAVE library
CAVEExtInit(); // initialize the CAVE extension library (required by probing library)
cpInit(...); // initialize the probing library
// register CAVE and probing callbacks here
// ... register displayCallback with both CAVE and probing lib
// ... register snappingCallback with probing lib
CAVEInit(); // initialize the CAVE library
while (run) {
cpUpdate(); // update the state of the probing library
}
cpQuit(); // quit probing lib (writes log files, frees resources)
cpExtQuit(); // quit cave extension library
CAVEExit();
}
void displayCallback() {
// clear openGL buffers, etc etc
cpDraw(); // draw the marker and other probing stuff (should always be first call in displaycallback)
// draw you own stuff
}
cpInit()
cpQuit()
cpUpdate()
cpDisplay()
cpSetCallback()
cpSetOption()
int cpInit(char *RetrieveSessionFilename, char *StoreSessionFilename,
char *HumanLogFilename, char *ComputerLogFilename,
char *SnapshotFilename);
Return value: non-zero on success.
This function must be called after CAVEConfigure(), but before CAVEInit(). It initializes the probing library. Make sure you have called CAVEExtInit() before you call cpInit(). The probing library uses CAVEExt locks, so the CAVEExt lib must be initialized when cpInit() is called! If an argument is NULL, it defaults to the value specified in the table below. For information about sessions, logfiles and their formats check out logs & file formats.
Argument |
Usage |
Defaults to |
RetrieveSessionFilename |
specifies the name of the file of the session you want to continue. If this argument is NULL, a new session will be started. |
- |
StoreSessionFilename |
specifies the name of the file where the session will be stored. If this argument is NULL, the session is not stored. |
- |
HumanLogFilename |
specifies the name of the file where the log intended for human use will be stored. |
probing.log |
ComputerLogFilename |
specifies the name of the file where the log intended for computer use will be stored. |
probing_computer.log |
SnapshotFilename |
specifies the basename of the files where snapshot will be stored |
snapshotX.rgb |
void cpQuit();
Return value: none
Call cpQuit() when your application wishes to terminate all probing activities. All resources allocated by the probing library will be freed. The measurementlogs and the sessions file will be stored on disk in the files specified.
void cpUpdate();
Return value: none
cpUpdate() function updates the state of the probing library. It should be called periodicly in the main loop of your CAVE application. For smooth interaction, make sure it is called at least 10x per second. It can only be called in the main process of your CAVE application, between the calls to cpInit() and cpQuit().
void cpDisplay();
Return value: none
cpDisplay() should be called by the drawing function of your CAVE application. It draws the probes and other visual information (but NOT the menu which is part of the probingUI library). It can only be called after cpInit() has been called. To make sure snapshots are made correctly, put this function at the start of your drawing function. Fyi: making snapshot is done by the probing library, not by the probingUI library.
void cpSetCallback(CP_CALLBACK_TYPE type, CP_FUNC function);
Return value: none
cpSetCallback() can be used to register callbacks. cpSetCallback() can be called (multiple times) between calls cpInit() and cpQuit(), in the main process of your CAVE application. The following table describes the callbacks:
type |
function |
CP_DISPLAY_FUNC |
Pass a pointer to a void f(void) function. This function must be the same as your CAVE display callback. It is used for making snapshots. |
CP_SNAP_FUNC |
Pass a pointer to a void f(float pos[3]) function. This function should snap pos (in navigated CAVE coordinates) to a meaningfull position (e.g. the vertices of a piece of geometry your application displays). Check out the sample code for an example. |
int cpSetOption(CP_OPTION option, const void *argument);
Return value: non-zero on success
cpSetOption() can be used to set options which not every user would want to set. Two important options are CP_USER_ARENA_SIZE and CP_USER_CREATED_ARENA. CP_USER_ARENA_SIZE allows you to set the size of the arena the probing library allocates it memory resources from, CP_USER_CREATED_ARENA allows you the specify an arbirary arena you created yourself with CAVEUserSharedMemory(). The use of CP_USER_CREATED_ARENA and CP_USER_ARENA_SIZE is mutually exclusive. You can't set both options.
option |
argument points to |
info |
CP_USER_ARENA_SIZE |
int |
Sets the size of the arena. Must be called before cpInit(), but after CAVEConfigure() |
CP_USER_CREATED_ARENA |
void (as returned by CAVEUserSharedMemory()) |
Sets a user created arena. Must be called before cpInit(), but after CAVEConfigure() |
CP_WAND_BUTTON |
int |
Sets which button is used for probing, Can be called any time between cpInit() and cpQuit(). Use [0..3] as argument |
CP_FNEAR |
float |
Sets glFrustum 'near' value (in feet) for snapshots. Argument is a pointer to a float. Can be called any time between cpInit() and cpQuit(). |
CP_FFAR |
float |
Sets glFrustum 'far' value (in feet) for snapshots. Argument is a pointer to a float. Can be called any time between cpInit() and cpQuit(). |
cpSelectChoice()
cpGetInterfaceChoiceName()
cpGetInterfaceChoiceProperty()
cpInterfaceChanged()
void cpSelectChoice(int n);
Return value: none
Selects choice number n from the current interface. If choice n is invalid nothing happens and no error is reported. This method should be called in response to a user selecting choice n somehow. The probingUI library calls cpSelectChoice() when the user pushes the button corresponding to that choice.
char *cpGetInterfaceChoiceName(int n);
int cpGetInterfaceChoiceProperty(CP_CHOICE_PROPERTY property, int n);
Return value of cpGetInterfaceChoiceName(): pointer to a ASCIIZ string, NULL if choice n does not exist
Return value of cpGetInterfaceChoiceProperty(): a value, -1 if choice n does not exist
These two functions allow you to retrieve information about the choices the current interface offers: cpGetInterfaceChoiceName() returns the name of choice n. Choices are numbered from [0 .. #choices-1]. cpGetInterfaceChoiceProperty() can be used to query choice-properties. The properties which can be retrieved are listed in the following table:
property |
description |
CP_BUTTON |
Queries whether choice n is a button (returns 1) or a interface (returns 0) |
CP_ENABLED |
Returns 1 if choice n is enabled. If a choice is disabled, it is futile to call cpSelectChoice() |
CP_NUMBER_OF_CHOICES |
Returns the number of choices for the current interface. The value of n is ignored. |
int cpInterfaceChanged();
Return value: non-zero if interface changed since last call.
cpInterfaceChanged() returns non-zero when the information in the current cpInterface has changed. If this is the case, you should update your user interface by calling cpGetInterfaceChoiceName() and cpGetInterfaceChoiceProperty() for every interface choice. cpInterfaceChanged() should be called regulary (> 10hz) to poll whether the interface has changed.
The value cpInterfaceChanged() returns is stored in the state of the library. This state is shared between all CAVE processes. When you call cpInterfaceChanged() the value is set to 0. This means that you can call cpInterfaceChanged() from one process only.
cpGetMeasurementString()
cpGetMeasurementIndex()
cpSetMeasurement
cpGetNumberOfMeasurements()
cpDeleteMeasurement()
cpSnapshotChanged()
char *cpGetMeasurementString(CP_MEASUREMENT_STRING s);
Return value: a pointer an ASCIIZ string
cpGetMeasurement() returns a pointer to the current measurement. NULL is returned if there is no current measurement. The possible values of CP_MEASUREMENT_STRING ar described below:
value |
description |
CP_HUMAN_TEXT |
human readable text belonging to this entry |
CP_COMPUTER_TEXT |
easy-to-parse text belonging to this entry |
CP_SNAPSHOT_FILENAME_LOWRES |
filename of file containing low resolution (256x256) snapshot |
CP_SNAPSHOT_FILENAME_HIGHRES |
filename of file containing high resolution (1024x768) snapshot |
int cpGetMeasurementIndex();
Return value: an integer value, -1 if there is no current measurement.
Use cpGetMeasurementIndex() to get the index of the current measurement in the list of measurements. This function can be used in combination with cpSetMeasurement(int Index) to 'browse' through the measurements (e.g. cpSetMeasurement(cpGetMeasurementIndex() + 1)).
void cpSetMeasurement(int index);
Return value: none
cpSetMeasurement(int Index) can be used to set the current measurement. The current measurement can be retreived with cpGetMeasurement(). If index is invalid, nothing happens and no error is reported
int cpGetNumberOfMeasurements();
Return value: an integer value
cpGetNumberOfMeasurements() returns the total number of measurements stored in the probing library
void cpDeleteMeasurement();
Return value: none
cpDeleteMeasurement() deletes the current measurement (if any). If there is no current measurement, nothing happens and no error is reported.
int cpSnapshotChanged();
Return value: non-zero if snapshot has changed.
This function return non-zero if the snapshot texture of the current measurement has changed since the last invokaction of cpSnapshotChanged(). When this is the case, you should reread the texture from disk (filename can be retrieved with cpGetMeasurementString()). Just like cpInterfaceChanged(), this function can only be called from one thread.
cpGetCalibrationBase()
cpSetCalibrationBase()
setUserMetrics()
void cpGetCalibrationBase(float pos[3], float rot[3], CP_METRIC *baseMetric, int *scale, int *draw);
Return value: none, values are stored in arguments
cpGetCalibrationBase() gets the calibration frame or base base. The position pos and rotation rot (standard CAVE yxz rotation order) specify the position and orientation relative to the navigated CAVE origin. The metric used is stored in baseMetric (can be CP_FEET, CP_METERS, CP_INCHES, CP_CENTIMETERS or CP_USERMETRICS). scale is used to return the scaling of the calibration frame (10^scale). draw is used to return whether the frame is drawn or not.
void cpSetCalibrationBase(float pos[3], float rot[3], CP_METRIC baseMetric, int scale, int draw);
Return value: none
cpSetCalibrationBase() sets the calibration frame. The arguments are the same those of cpGetCalibrationBase().
void cpSetUserMetrics(float lengthInMeters, char *name);
Return value: none
cpSetUserMetrics() sets the name and lenght in meters of the user metric. The user metric can be used if your application needs some non-standard metric (e.g. lightyears).
The probingUI library is a layer on top of the probing library. The probingUI library displays an interactive console and handles speech input via a speech server. It's source code is also a nice example (except for the overkill on error checking...) for people who want to integrate the probing library into their own menu. The sample application is an example of how to use the probingUI library. Note that EVERY function of the probingUI library must be called at the right place in your CAVE application. This documentation and sample application show where and how this should happen.
The probingUI library handles a lot for you, so most calls to the probing library are not safe to use anymore (since the probingUI library calls them for you). To avoid confusing the library or causing errors do not call any probing functions except: cpSetOption(), cpSetCallback(), cpGetCalibrationBase(), cpSetCalibrationBase() and setUserMetrics(). Also don't call CAVEExtInit() or CAVEExtQuit(). probingUI manages this for you. Here follows a description of the probingUI API functions:
cpuiInit()
cpuiSetup()
cpuiUpdate()
cpuiQuit()
cpuiInitGraphics()
cpuiPerFrame()
cpuiDisplay()
int cpuiInit(char *RecoverSessionFilename, char *StoreSessionFilename,
char *HumanLogFilename, char *ComputerLogFilename,
char *SnapshotFilename, char *SpeechServerAddress);
Return value: non-zero on success
cpuiInit() must be called after CAVEConfigure(), but before CAVEInit(). It initializes the probingUI and probing libraries. All arguments are the same as cpInit() except for SpeechServerAddress. This must be the tcp address (servername:port) of a speech server, or NULL if you don't want to use speech.
int cpuiSetup();
Return value: non-zero on success
cpuiSetup sets up the probing lib user interface. Must be called after cpuiInit() and CAVEInit(), in the main process.
int cpuiUpdate();
Return value: non-zero on success
cpuiUpdate() updates the user interface of the probing lib. Must be called frequently (> 10hz for smooth interaction) in the main loop of your main process.
int cpuiQuit();
Return value: non-zero on success
cpuiQuit() must be called when you want to quit the probing lib. It closes the logs, stores the sessions (if a StoreSessionFilename was given to cpuiInit()), and frees all resources. If you fail to call this function, your session and logs will not be stored.
int cpuiInitGraphics();
Return value: non-zero on success
cpuiInitGraphics() initializes the graphics (e.g. the truetype lib) used by the probing lib. Call it in the CAVE_INITGRAPHICS_CALLBACK of your cave application. Must be called in all your drawing processes of your CAVE application,
int cpuiPerFrame();
Return value: non-zero on success
cpuiPerFrame() must be called in the 'per frame' callback of your CAVE application (CAVE_PERFRAME_CALLBACK).
int cpuiDisplay();
Return value: non-zero on success
Call cpuiDisplay() in the display callback of your CAVE application CAVE_DISPLAY_CALLBACK. It draws the probing lib user interface.
To use the probing library, you must include probing.h. To use the probingUI library, include probingUI.h. Below is a table listing the libraries which the probing library and the probingUI library require:
probing or probingUI? |
Requires library: |
Because: |
both |
cClasses |
probingUI uses cString, probing uses cString, cList and cTokenizer |
both |
CAVEExtLocks, CAVEExtPLM, CAVEExt math |
|
probingUI |
cfc |
Used for console |
probingUI |
Used for speech input |
|
probingUI |
cfc requires it for drawing truetype fonts |
|
probingUI |
cfc requires it for drawing truetype fonts |
|
probingUI |
duh |
Measurement log for human use
Measurement log for computer use
Snapshot fileformat
Session fileformat
Measurement log for human use
All measurement you make (except those you delete...) are stored in two logs. One log is for human use and contains exactly the same texts you can read on the probingUI console, plus references to a possible snapshot. The other log is for computer use and contains easier-to-parse and more detailed information. The format of the human and computer logs is so simple, you can understand it by reading an example, so here we go:
measurement 0
trace measurement (metric: meters):
length: 3.58 meters
marker 0: (-0.71 2.53 0.00)
marker 1: (-0.40 1.90 0.00)
marker 2: (-0.15 2.58 0.00)
marker 3: (-0.79 2.17 0.00)
marker 4: (-0.08 2.19 0.00)
marker 5: (-0.71 2.53 0.00)
lowres snapshot: snapshot_lowres_0.rgb
highres snapshot: snapshot_highres_0.rgb
measurement 1
distance measurement:
marker 1: (-0.79 2.17 0.00) meters
marker 2: (-0.15 2.58 0.00) meters
distance: 0.76 meters
lowres snapshot:
highres snapshot:
measurement 2
angle measurement:
base: (-0.71 2.53 0.00) meters
direction 1: (-0.40 1.90 0.00) meters
direction 2: (-0.15 2.58 0.00) meters
angle: 68.79 degrees
lowres snapshot:
highres snapshot:
Measurement log for computer use
The computer measurement log contains values of higher precision and is easier to parse. Here is an example containing the same measurements as the example above
0
low: snapshot_highres_0.rgb
high: snapshot_highres_0.rgb
trace 6 markers
length 3.577299e+000 meters
-7.075723e-001 2.526382e+000 1.055680e-006 meters
-3.991435e-001 1.904213e+000 7.746473e-007 meters
-1.511914e-001 2.576659e+000 9.028407e-007 meters
-7.861912e-001 2.168164e+000 9.712433e-007 meters
-8.466733e-002 2.187017e+000 7.650826e-007 meters
-7.075723e-001 2.526382e+000 1.055680e-006 meters
end
1
low:
high:
distance
-7.861912e-001 2.168164e+000 9.712433e-007 meters
-1.511914e-001 2.576659e+000 9.028407e-007 meters
7.550451e-001 meters
end
2
low:
high:
angle
-7.075723e-001 2.526382e+000 1.055680e-006 meters
-3.991435e-001 1.904213e+000 7.746473e-007 meters
-1.511914e-001 2.576659e+000 9.028407e-007 meters
6.879436e+001
end
Snapshot fileformat
The format for the snapshot .rgb files is very simple. The first two bytes are the width of the snapshot in big endian order. The next two bytes are the height of the snapshot. The rest of the file consists of width * height * 3 bytes representing the RGB value of every pixel in the snapshot (unsigned bytes, range [0...255]). The image is stored in left-right top-down order.
Session fileformat
A session file is stored when you call cpQuit(). This sessions contains information about the settings of the library at the time you quit, like the position of the calibration frame, the metric used, the various options you can set in the marker interface, etc, etc. Measurements are not stored in session files. When the probing library is initialized, the session file a read from disk and all settings are restored.
Session files are not meant to be modified by hand (though it's possible of course). One usefull thing you could do however is preset all markers you want to use during a session, so you only have to select them when you do the actual measurements. Here is an example of a session file (from the session where I made the measurements shown above). If you store this text in a file, you can retrieve my session.
<session>
date = 01 - 22 - 1999 09 : 29 PM
<calibration>
draw base = 1
scale = 0
metric = 1
user metric name = "default"
user metric length = 1.000000e+000
pos = -5.873019e-001 1.500000e-001 -2.600000e+000
rot = 6.283185e+000 6.283185e+000 6.283185e+000
</calibration>
<measurement>
snapshot angle = 0
</measurement>
<marker>
marker = -2.908731e+000 8.438657e+000 -2.600000e+000
marker = -1.896826e+000 6.397419e+000 -2.600000e+000
marker = -1.083334e+000 8.603605e+000 -2.600000e+000
marker = -3.166667e+000 7.263399e+000 -2.600000e+000
marker = -8.650797e-001 7.325254e+000 -2.600000e+000
auto mode = 2
snap = 0
scale = 0
scale factor = 1.000000e-001
generate events = 1
current marker = 2
distance markers = 3 2
angle markers = 0 1 2
trace markers = 0 1 2 3 4 0
</marker>
</session>