Leap Motion C API  3.1
The API to the LeapC library.
Textured Quad Example

The Textured Quad example textures a standard OpenGL quad with the image set from the Leap Motion cameras. Both images are included in one texture, with the left image above the right image.

The example uses callbacks to get tracking frames. When a frame becomes available, the example requests the corresponding image. When LeapC calls the image complete callback, the example sets an "imageReady" state variable. The example's GLUT idle function monitors the imageReady variable, and tells GLUT to redraw the display when the variable evaluates as true. The drawing is then performed when GLUT calls the display() function.

The quad is drawn using the OpenGL fixed function pipeline for simplicity. In all but the simplest cases, you should use the modern shader-based pipeline instead. Using shaders will also give you the ability to correct the image distortion for very little cost.

1 #undef __cplusplus
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 
6 #ifdef _WIN32
7 #include <Windows.h>
8 #else
9 #include <unistd.h>
10 #endif
11 
12 #include <time.h>
13 #include "LeapC.h"
14 #include "ExampleConnection.h"
15 #include "GLutils.h"
16 
17 LEAP_CONNECTION *connection;
18 
19 LEAP_IMAGE_FRAME_REQUEST_TOKEN *image_token = 0;
20 void* image_buffer = NULL;
21 uint64_t image_size = 1;
22 bool imageRequested = false;
23 bool imageReady = false;
24 bool textureChanged = false;
25 uint32_t image_width = 0;
26 uint32_t image_height = 0;
27 GLuint texture = 0;
28 
29 int window; // GLUT window handle
30 
31 /** Callback for when an image request completes. */
32 void OnImages(const LEAP_IMAGE_COMPLETE_EVENT *imageCompleteEvent){
33  if(image_width != imageCompleteEvent->properties->width ||
34  image_height != imageCompleteEvent->properties->height){
35  image_width = imageCompleteEvent->properties->width;
36  image_height = imageCompleteEvent->properties->height;
37  textureChanged = true;
38  }
39  free(image_token);
40  image_token = 0;
41  imageRequested = false;
42  imageReady = true;
43 }
44 
45 /** Callback for when an image request fails. */
46 void OnImageError(const LEAP_IMAGE_FRAME_REQUEST_ERROR_EVENT *imageErrorEvent){
47  if(imageErrorEvent->error == eLeapImageRequestError_ImagesDisabled)
48  printf("Warning: Images disabled. Check your control panel settings.");
49 
50  //Resize image buffer if too small
51  if(image_size < imageErrorEvent->required_buffer_len){
52  image_size = imageErrorEvent->required_buffer_len;
53  if(image_buffer) free(image_buffer);
54  image_buffer = malloc((size_t)image_size);
55  printf("Resized image buffer to %lli.\n", (long long int)image_size);
56  }
57 
58  free(image_token);
59  image_token = NULL;
60  imageRequested = false;
61  imageReady = false;
62 }
63 
64 /* Notifies us that a new frame is available. */
65 void OnFrame(const LEAP_TRACKING_EVENT *frame){
66  if(!imageRequested && !imageReady){
67  imageRequested = true;
68  LEAP_IMAGE_FRAME_DESCRIPTION frameDescription;
69  frameDescription.type = eLeapImageType_Default;
70  frameDescription.frame_id = frame->info.frame_id;
71  frameDescription.buffer_len = image_size;
72  frameDescription.pBuffer = image_buffer;
73 
74  image_token = malloc(sizeof(LEAP_IMAGE_FRAME_REQUEST_TOKEN));
75  eLeapRS result = LeapRequestImages(*connection, &frameDescription, image_token);
76  if(result != eLeapRS_Success)
77  printf("LeapRequestImages call was %s.\n", ResultString(result));
78  }
79 }
80 
81 // Draw a textured quad displaying the image data
82 void DrawImageQuad(float p1X, float p1Y, float p2X, float p2Y, int width, int height, void* imagedata){
83  glEnable(GL_TEXTURE_2D);
84  if(textureChanged){
85  textureChanged = false;
86  glDeleteTextures(1, &texture);
87  glGenTextures(1, &texture);
88  glBindTexture(GL_TEXTURE_2D, texture);
89  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
90  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imagedata);
91  checkGLError("Initializing texture.");
92  } else { //update existing texture
93  glBindTexture ( GL_TEXTURE_2D, texture);
94  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, imagedata);
95  checkGLError("Updating texture.");
96  }
97  //Draw a texture-mapped quad
98  glBegin(GL_QUADS);
99  glTexCoord2f(1, 1); glVertex2f((GLfloat)p1X, (GLfloat)p1Y);
100  glTexCoord2f(0, 1); glVertex2f((GLfloat)p2X, (GLfloat)p1Y);
101  glTexCoord2f(0, 0); glVertex2f((GLfloat)p2X, (GLfloat)p2Y);
102  glTexCoord2f(1, 0); glVertex2f((GLfloat)p1X, (GLfloat)p2Y);
103  glEnd();
104  checkGLError("Drawing quad.");
105  glDisable(GL_TEXTURE_2D);
106 }
107 // Done drawing quad
108 
109 void display(void)
110 {
111  glMatrixMode(GL_MODELVIEW);
112  glPushMatrix();
113  glTranslatef(-32, -24, -50); //"Camera" viewpoint
114  glClear(GL_COLOR_BUFFER_BIT);
115  if(imageReady){
116  DrawImageQuad(0, 0, 64, 48, image_width, image_height * 2, image_buffer);
117  imageReady = false;
118  }
119  glFlush();
120  glPopMatrix();
121  glutSwapBuffers();
122 }
123 
124 void reshape(int w, int h)
125 {
126  glViewport(0, 0, (GLsizei) w, (GLsizei) h);
127  glMatrixMode(GL_PROJECTION);
128  glLoadIdentity();
129  gluPerspective(60, 640/240, 1.0, 1000);
130 }
131 
132 void keyboard(unsigned char key, int x, int y)
133 {
134  switch((char)key) {
135  case 'q':
136  case 27: // ESC
137  glutDestroyWindow(window);
138  if(imageRequested)
139  LeapCancelImageFrameRequest(*connection, *image_token);
140  if(image_buffer) free(image_buffer);
141  CloseConnection();
142  exit(0);
143  default:
144  break;
145  }
146 }
147 
148 void idle(void){
149  if(imageReady)
150  glutPostRedisplay();
151 }
152 
153 int main(int argc, char *argv[])
154 {
155  ConnectionCallbacks.on_frame = OnFrame;
156  ConnectionCallbacks.on_image_complete = OnImages;
157  ConnectionCallbacks.on_image_request_error = OnImageError;
158 
159  connection = OpenConnection();
160 
161  while(!IsConnected){
162  millisleep(250);
163  }
164 
165  glutInit(&argc, argv);
166  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
167  glutInitWindowSize(640, 480);
168  window = glutCreateWindow("LeapC Image Example");
169 
170  // GLUT callbacks
171  glutIdleFunc(idle);
172  glutReshapeFunc(reshape);
173  glutKeyboardFunc(keyboard);
174  glutDisplayFunc(display);
175 
176  // init GL
177  glClearColor(0.0, 0.0, 0.0, 0.0);
178  glColor3f(1.0, 1.0, 1.0);
179 
180  // Start GLUT loop
181  glutMainLoop();
182 
183  CloseConnection();
184  return 0;
185 }
186 //End-of-Sample

This example is only supported on platforms for which a working version of GLUT exists. It should not be overly difficult to port the example to a different OpenGL-based context, however.