| Tilt Five NDK |

Getting Started : C

# Project Setup

The following files need to be included in your project

File(s) | Purpose
--- | ---
.h files from the `include` directory | C header files for the API
One of the libraries from `lib` directory | The Tilt Five NDK support library

# Using the Glasses

## Service Connection

Most of the operations performed with the glasses are not done directly by the client, but are performed by the Tilt Five™ service on behalf of the client.

The connection to the service is not guaranteed to always be available. Scenarios where the service is unavailable include:

- During client startup there can be a small delay before the connection is established.
- The service is not installed (e.g. User hasn't installed the Tilt Five™ drivers).
- The service may have crashed or been stopped by a user. As such, all queries should anticipate a response of T5_ERROR_NO_SERVICE and retry appropriately (assuming it's a transient condition).

A trivial example of handling this is shown below:

433 bool waiting = false;
434 for (;;) {
435 char serviceVersion[T5_MAX_STRING_PARAM_LEN];
436 size_t bufferSize = T5_MAX_STRING_PARAM_LEN;
437 err = t5GetSystemUtf8Param(
438 t5ctx, kT5_ParamSys_UTF8_Service_Version, serviceVersion, &bufferSize);
439 if (!err) {
440 printf("Service version : %s\n", serviceVersion);
441 break;
442 }
443
444 if (err == T5_ERROR_NO_SERVICE) {
445 if (!waiting) {
446 printf("Waiting for service...\n");
447 waiting = true;
448 }
449 } else {
450 printf("Error getting service version : %s\n", t5GetResultMessage(err));
451 exit(EXIT_FAILURE);
452 }
453 }
t5GetResultMessage
T5_EXPORT const char * t5GetResultMessage(T5_Result result)
T5_ERROR_NO_SERVICE
#define T5_ERROR_NO_SERVICE
Service isn't connected.
Definition: errors.h:72
t5GetSystemUtf8Param
T5_EXPORT T5_Result t5GetSystemUtf8Param(T5_Context context, T5_ParamSys param, char *buffer, size_t *bufferSize)
Get a system-wide UTF-8 encoded string parameter.
T5_MAX_STRING_PARAM_LEN
#define T5_MAX_STRING_PARAM_LEN
The maximum number of characters allowed for string values.
Definition: types.h:38
kT5_ParamSys_UTF8_Service_Version
@ kT5_ParamSys_UTF8_Service_Version
Version of the service software - UTF8.
Definition: types.h:431

## Threading

All calls are blocking, and their thread safety is documented on each call. Generally, calls are grouped into a number of groups. From each group only one function should should be called concurrently. Calls may be made to functions in different groups concurrently. Additionally, some calls require specific threads (such as graphics related functions like t5InitGlassesGraphicsContext() and t5SendFrameToGlasses()).

## Operations

Depending on the level of access required to the API, there are one or more setup steps. Details are shown below with example code. Further details of the API are here documented in the Tilt Five™ Native Interface (C).

## Create Context

402 T5_ClientInfo clientInfo = {
403 .applicationId = "com.MyCompany.MyApplication",
404 .applicationVersion = "1.0.0",
405 .sdkType = 0,
406 .reserved = 0,
407 };
408
409 T5_Context t5ctx;
410 T5_Result err = t5CreateContext(&t5ctx, &clientInfo, 0);
411 if (err) {
412 printf("Failed to create context\n");
413 }
T5_Result
uint32_t T5_Result
Represents an error code that may be returned by the Tilt Five™ API.
Definition: errors.h:47
t5CreateContext
T5_EXPORT T5_Result t5CreateContext(T5_Context *context, const T5_ClientInfo *clientInfo, void *platformContext)
Create a context object.
T5_Context
struct T5_ContextImpl * T5_Context
Opaque handle used with system-wide functions.
Definition: types.h:71
T5_ClientInfo
Client provided information for use with t5CreateGlasses()
Definition: types.h:136
T5_ClientInfo::applicationId
const char * applicationId
The application ID.
Definition: types.h:138

### Release Context

The context should be released after it's finished with

505 t5DestroyContext(&t5ctx);
t5DestroyContext
T5_EXPORT void t5DestroyContext(T5_Context *context)
Destroy a context object.

## System-wide Queries

System-wide queries require nothing more than a client to perform. Most of these operations are querying the service for common parameters such as the service version (t5GetSystemUtf8Param() and fixed physical parameters (t5GetGameboardSize()).

419 T5_GameboardSize gameboardSize;
420 err = t5GetGameboardSize(t5ctx, kT5_GameboardType_LE, &gameboardSize);
421 if (err) {
422 printf("Failed to get gameboard size\n");
423 } else {
424 printf("LE Gameboard size : %fm x %fm x %fm\n",
425 gameboardSize.viewableExtentPositiveX + gameboardSize.viewableExtentNegativeX,
426 gameboardSize.viewableExtentPositiveY + gameboardSize.viewableExtentNegativeY,
427 gameboardSize.viewableExtentPositiveZ);
428 }
t5GetGameboardSize
T5_EXPORT T5_Result t5GetGameboardSize(T5_Context context, T5_GameboardType gameboardType, T5_GameboardSize *gameboardSize)
Get the gameboard dimensions.
kT5_GameboardType_LE
@ kT5_GameboardType_LE
An LE gameboard.
Definition: types.h:103
T5_GameboardSize
Physical dimensions of a gameboard.
Definition: types.h:113
T5_GameboardSize::viewableExtentNegativeY
float viewableExtentNegativeY
The distance in meters from the gameboard origin to the edge of the viewable area in the negative Y d...
Definition: types.h:128
T5_GameboardSize::viewableExtentPositiveZ
float viewableExtentPositiveZ
The distance in meters above the gameboard origin that the viewable area extends in the positive Z di...
Definition: types.h:132
T5_GameboardSize::viewableExtentPositiveY
float viewableExtentPositiveY
The distance in meters from the gameboard origin to the edge of the viewable area in the positive Y d...
Definition: types.h:124
T5_GameboardSize::viewableExtentNegativeX
float viewableExtentNegativeX
The distance in meters from the gameboard origin to the edge of the viewable area in the negative X d...
Definition: types.h:120
T5_GameboardSize::viewableExtentPositiveX
float viewableExtentPositiveX
The distance in meters from the gameboard origin to the edge of the viewable area in the positive X d...
Definition: types.h:116

## Enumerate Glasses

457 for (;;) {
458 size_t bufferSize = GLASSES_BUFFER_SIZE;
459 char glassesListBuffer[GLASSES_BUFFER_SIZE];
460 err = t5ListGlasses(t5ctx, glassesListBuffer, &bufferSize);
461 if (!err) {
462 size_t glassesCount = 0;
463
464 const char* buffPtr = glassesListBuffer;
465 for (;;) {
466 // Get the length of the string and exit if we've read the
467 // terminal string (Zero length)
468 size_t stringLength = strnlen(buffPtr, GLASSES_BUFFER_SIZE);
469 if (stringLength == 0) {
470 break;
471 }
472
473 fprintf(stderr, "Glasses : %s\n", buffPtr);
474 glassesCount++;
475
t5ListGlasses
T5_EXPORT T5_Result t5ListGlasses(T5_Context context, char *buffer, size_t *bufferSize)
Enumerate all glasses.

At this point `buffPtr` contains a glasses identifier suitable for Create Glasses 'creating/selecting glasses'.

479
480 // Advance through the returned values
481 buffPtr += stringLength;
482 if (buffPtr > (glassesListBuffer + GLASSES_BUFFER_SIZE)) {
483 printf("Warning: list may be missing null terminator");
484 break;
485 }
486 }
487
488 printf("Listed glasses [%zu found]\n", glassesCount);
489 break;
490 }
491
492 if (err == T5_ERROR_NO_SERVICE) {
493 if (!waiting) {
494 printf("Waiting for service...\n");
495 waiting = true;
496 }
497 } else {
498 printf("Error listing glasses : %s\n", t5GetResultMessage(err));
499 exit(EXIT_FAILURE);
500 }
501 }

## Create Glasses

Creating glasses handles requires a hardware identifier (`id` below) for the glasses as well as some information about the client.

277
278 // CREATE GLASSES
279 T5_Glasses glassesHandle;
280 T5_Result err = t5CreateGlasses(t5ctx, id, &glassesHandle);
281 if (err) {
282 printf("Error creating glasses '%s' : %s\n", id, t5GetResultMessage(err));
283 return;
284 } else {
285 printf("Created glasses : '%s'\n", id);
286 }
t5CreateGlasses
T5_EXPORT T5_Result t5CreateGlasses(T5_Context context, const char *id, T5_Glasses *glasses)
Create a glasses access object.
T5_Glasses
struct T5_GlassesImpl * T5_Glasses
Opaque handle used with glasses.
Definition: types.h:77

## Non-exclusive Operations

Some glasses operations are available without acquiring exclusive access. These include operations to query stored parameters about glasses such as the IPD (t5GetGlassesFloatParam()) and user specified friendly name (t5GetGlassesUtf8Param()). Additionally, accessing the Wand Stream 'wand stream' does not require an exclusive connection.

222 T5_Result endlesslyWatchSettings(T5_Glasses glassesHandle, const char* id) {
223 T5_Result err;
224
225 printf("Watching for changes to settings... (forever)\n");
226 for (;;) {
227 uint16_t count = PARAM_BUFFER_SIZE;
228 T5_ParamGlasses paramBuffer[PARAM_BUFFER_SIZE];
229 err = t5GetChangedGlassesParams(glassesHandle, paramBuffer, &count);
230 if (err) {
231 printf("Error getting changed params for '%s' : %s\n", id, t5GetResultMessage(err));
232 return err;
233 }
234
235 // Get the values of the changed params
236 for (int i = 0; i < count; i++) {
237 switch (paramBuffer[i]) {
238 case kT5_ParamGlasses_Float_IPD: {
239 double value = 0.0;
240 err = t5GetGlassesFloatParam(glassesHandle, 0, paramBuffer[i], &value);
241 if (err) {
242 printf("Error getting changed IPD for '%s' : %s\n",
243 id,
244 t5GetResultMessage(err));
245 return err;
246 }
247
248 printf("IPD changed for '%s' : %f\n", id, value);
249 } break;
250
251 case kT5_ParamGlasses_UTF8_FriendlyName: {
252 char buffer[T5_MAX_STRING_PARAM_LEN];
253 size_t bufferSize = T5_MAX_STRING_PARAM_LEN;
254 err = t5GetGlassesUtf8Param(
255 glassesHandle, paramBuffer[i], 0, buffer, &bufferSize);
256 if (err) {
257 printf("Error getting changed friendly name for '%s' : %s\n",
258 id,
259 t5GetResultMessage(err));
260 return err;
261 }
262
263 printf("Friendly name changed for '%s' : '%s'\n", id, buffer);
264 } break;
265
266 default:
267 // Ignore other parameters
268 break;
269 }
270 }
271 }
272}
t5GetGlassesFloatParam
T5_EXPORT T5_Result t5GetGlassesFloatParam(T5_Glasses glasses, T5_WandHandle wand, T5_ParamGlasses param, double *value)
Get a glasses floating point parameter.
t5GetGlassesUtf8Param
T5_EXPORT T5_Result t5GetGlassesUtf8Param(T5_Glasses glasses, T5_WandHandle wand, T5_ParamGlasses param, char *buffer, size_t *bufferSize)
Get a glasses UTF-8 encoded string parameter.
t5GetChangedGlassesParams
T5_EXPORT T5_Result t5GetChangedGlassesParams(T5_Glasses glasses, T5_ParamGlasses *buffer, uint16_t *count)
Get a glasses-specific list of changed parameters.
T5_ParamGlasses
T5_ParamGlasses
Possible parameters that can be retrieved for glasses.
Definition: types.h:419
kT5_ParamGlasses_Float_IPD
@ kT5_ParamGlasses_Float_IPD
Interpupillary distance - Float, millimeters
Definition: types.h:422
kT5_ParamGlasses_UTF8_FriendlyName
@ kT5_ParamGlasses_UTF8_FriendlyName
User-facing name of the glasses - UTF8.
Definition: types.h:425
count
constexpr iterator_traits< _InputIterator >::difference_type count(_InputIterator __first, _InputIterator __last, const _Tp &__value)

## Reserve for Exclusive

Other operations require exclusive access before a client can perform them. Only one client can have exclusive access to glasses at a time. Principally, obtaining the current pose (t5GetGlassesPose()) and sending frames for rendering (t5SendFrameToGlasses()) require exclusive access.

Essentially, this is a process of repeatedly calling t5ReserveGlasses() until the function returns success. This can succeed even if glasses are not fully available (for example due to rebooting). Once the glasses are marked exclusive, then it is time to ensure the glasses are ready for exclusive operations.

295 err = t5ReserveGlasses(glassesHandle, "My Awesome Application");
296 if (err) {
297 printf("Error acquiring glasses '%s': %s\n", id, t5GetResultMessage(err));
298 return;
299 }
t5ReserveGlasses
T5_EXPORT T5_Result t5ReserveGlasses(T5_Glasses glasses, const char *displayName)
Reserve glasses for exclusive operations by the client.

## Ensure Ready for Exclusive

After a pair of glasses has been reserved, they need to be made fully ready for exclusive operations (e.g., getting the current glasses pose and sending frames) using `t5EnsureGlassesReady()`. This may return an error that retry is needed if the glasses are not yet ready. Upon success, the glasses are ready for exclusive operations.

310 // ENSURE GLASSES ARE READY
311 for (;;) {
312 err = t5EnsureGlassesReady(glassesHandle);
313 if (err) {
314 if (err == T5_ERROR_TRY_AGAIN) {
315 // A non-sample program would probably sleep here or retry on another frame pass.
316 continue;
317 }
318 printf("Error ensure glasses ready '%s': %s\n", id, t5GetResultMessage(err));
319 return;
320 }
321
322 printf("Glasses ready : '%s'\n", id);
323 break;
324 }
T5_ERROR_TRY_AGAIN
#define T5_ERROR_TRY_AGAIN
Target is not currently available.
Definition: errors.h:120
t5EnsureGlassesReady
T5_EXPORT T5_Result t5EnsureGlassesReady(T5_Glasses glasses)
Ensure that reserved glasses are ready for exclusive operations.

## Exclusive Operations

Once an exclusive connection has been established, you may perform exclusive operations such as t5GetGlassesPose() and t5SendFrameToGlasses().

172 void getPoses(T5_Glasses glassesHandle) {
173 T5_Result err;
174 T5_GlassesPose pose;
175
176 printf("Getting some poses");
177
178 bool waiting = false;
179 for (int i = 0; i < 1000; i++) {
180 err = t5GetGlassesPose(glassesHandle, kT5_GlassesPoseUsage_GlassesPresentation, &pose);
181 if (err) {
182 if (err == T5_ERROR_TRY_AGAIN) {
183 if (!waiting) {
184 printf("\nWaiting...");
185 fflush(stdout);
186 } else {
187 printf(".");
188 fflush(stdout);
189 }
190 SLEEP(100);
191 i--;
192 waiting = true;
193 continue;
194 } else {
195 printf("Failed to get pose : %s\n", t5GetResultMessage(err));
196 return;
197 }
198 }
199
200 waiting = false;
201
202 printf("\nGLASSES POSE : [%lu] (Board:%s) "
203 "[%6.4f, %6.4f, %6.4f] [%6.4f, %6.4f, %6.4f, %6.4f]",
204 pose.timestampNanos,
205 gameboardTypeString(pose.gameboardType),
206 pose.posGLS_GBD.x,
207 pose.posGLS_GBD.y,
208 pose.posGLS_GBD.z,
209 pose.rotToGLS_GBD.w,
210 pose.rotToGLS_GBD.x,
211 pose.rotToGLS_GBD.y,
212 pose.rotToGLS_GBD.z);
213
214 SLEEP(16);
215 }
216
217 printf("\n");
218}
t5GetGlassesPose
T5_EXPORT T5_Result t5GetGlassesPose(T5_Glasses glasses, T5_GlassesPoseUsage usage, T5_GlassesPose *pose)
Get the latest pose of the glasses.
kT5_GlassesPoseUsage_GlassesPresentation
@ kT5_GlassesPoseUsage_GlassesPresentation
The pose will be used to render images to be presented on the glasses.
Definition: types.h:176
T5_GlassesPose
Glasses pose information to be retrieved with t5GetGlassesPose()
Definition: types.h:199
T5_GlassesPose::timestampNanos
uint64_t timestampNanos
The timestamp of the pose.
Definition: types.h:201
T5_GlassesPose::posGLS_GBD
T5_Vec3 posGLS_GBD
The position of the origin of the GLS (glasses) frame relative to the GBD (gameboard) frame.
Definition: types.h:205
T5_GlassesPose::rotToGLS_GBD
T5_Quat rotToGLS_GBD
The rotation that transforms points in the GBD (gameboard) frame orientation to the GLS (glasses) fra...
Definition: types.h:209
T5_GlassesPose::gameboardType
T5_GameboardType gameboardType
The type of gameboard visible for this pose.
Definition: types.h:212

## Release Exclusive Access

At any point after glasses have been reserved by a client, they can be released so that another client can reserve them instead without needing to destroy the whole glasses object or context. Use t5ReleaseGlasses() to restore availability to other clients.

372 // RELEASE THE GLASSES
373 t5ReleaseGlasses(glassesHandle);
t5ReleaseGlasses
T5_EXPORT T5_Result t5ReleaseGlasses(T5_Glasses glasses)

# Wand Stream

Information about wand events is not delivered separately for each connected wand, but is instead multiplexed into a glasses global stream. Stream events are one of the following (T5_WandStreamEventType):

Event | Meaning
--- | ---
Connect | Glasses have detected a wand has connected.
Disconnect | Glasses have detected a wand disconnection.
Desync | The wand stream has desynchronized, and the client may have missed one or more connection, disconnection or report packets.
Report | A T5_WandReport is available detailing button, analog and positional data for a wand.

Simplistically, accessing the wand stream is done by configuring the stream (t5ConfigureWandStreamForGlasses()) and then repeatedly polling the stream (t5ReadWandStreamForGlasses()).

101 if (count > 0) {
102 // Enable wand stream
103 T5_WandStreamConfig config;
104 config.enabled = true;
105 err = t5ConfigureWandStreamForGlasses(glassesHandle, &config);
106 if (err) {
107 printf("Failed to enable stream : %s\n", t5GetResultMessage(err));
108 return;
109 }
110
111 T5_WandStreamEvent event;
112 for (int i = 0; i < 100; i++) {
113 err = t5ReadWandStreamForGlasses(glassesHandle, &event, 100);
114 if (err) {
115 if (err == T5_TIMEOUT) {
116 continue;
117 }
118
119 printf("Failed to read stream : %s\n", t5GetResultMessage(err));
120 return;
121 }
122
123 switch (event.type) {
124 case kT5_WandStreamEventType_Connect:
125 printf("WAND EVENT : CONNECT [%u]\n", event.wandId);
126 break;
127
128 case kT5_WandStreamEventType_Disconnect:
129 printf("WAND EVENT : DISCONNECT [%u]\n", event.wandId);
130 break;
131
132 case kT5_WandStreamEventType_Desync:
133 printf("WAND EVENT : DESYNC [%u]\n", event.wandId);
134 break;
135
136 case kT5_WandStreamEventType_Report:
137 printf("WAND EVENT : REPORT [%u] %fx%f\n",
138 event.wandId,
139 event.report.stick.x,
140 event.report.stick.y);
141 break;
142 }
143 }
144
145 // Disable wand stream
146 config.enabled = false;
147 err = t5ConfigureWandStreamForGlasses(glassesHandle, &config);
148 if (err) {
149 printf("Failed to disable stream : %s\n", t5GetResultMessage(err));
150 return;
151 }
152 }
T5_TIMEOUT
#define T5_TIMEOUT
Timeout.
Definition: errors.h:57
t5ReadWandStreamForGlasses
T5_EXPORT T5_Result t5ReadWandStreamForGlasses(T5_Glasses glasses, T5_WandStreamEvent *event, uint32_t timeoutMs)
Read from the wands event stream.
t5ConfigureWandStreamForGlasses
T5_EXPORT T5_Result t5ConfigureWandStreamForGlasses(T5_Glasses glasses, const T5_WandStreamConfig *config)
Configure the wand event stream.
kT5_WandStreamEventType_Connect
@ kT5_WandStreamEventType_Connect
Wand connected.
Definition: types.h:321
kT5_WandStreamEventType_Disconnect
@ kT5_WandStreamEventType_Disconnect
Wand disconnected.
Definition: types.h:324
kT5_WandStreamEventType_Report
@ kT5_WandStreamEventType_Report
Wand report (Pose, Buttons, Trigger, Stick, Battery)
Definition: types.h:330
kT5_WandStreamEventType_Desync
@ kT5_WandStreamEventType_Desync
Stream has desynchronized.
Definition: types.h:327
T5_WandStreamConfig
Wand stream configuration.
Definition: types.h:312
T5_WandStreamConfig::enabled
bool enabled
Enable or disable the entire stream. True = enabled.
Definition: types.h:314
T5_WandReport::stick
T5_Vec2 stick
Stick (X/Y) - Analog, Range [-1.0 - 1.0], 0 = Centered, 1.0 = Top/Right.
Definition: types.h:366
T5_WandStreamEvent
Represents an event from the wand stream.
Definition: types.h:404
T5_WandStreamEvent::report
T5_WandReport report
Report (Valid if type = kT5_WandStreamEventType_Report)
Definition: types.h:415
T5_WandStreamEvent::type
T5_WandStreamEventType type
Type of event.
Definition: types.h:409
T5_WandStreamEvent::wandId
T5_WandHandle wandId
Opaque identifier for the wand.
Definition: types.h:406