Leap Motion C API  4.1.0
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 <string.h>
14 #include "LeapC.h"
15 #include "ExampleConnection.h"
16 #include "GLutils.h"
17 
18 void* image_buffer = NULL;
19 uint64_t image_size = 0;
20 bool imageReady = false;
21 bool textureChanged = false;
22 uint32_t image_width = 0;
23 uint32_t image_height = 0;
24 GLuint texture = 0;
25 int window; // GLUT window handle
26 
27 /** Callback for when an image is available. */
28 static void OnImage(const LEAP_IMAGE_EVENT *imageEvent){
29  const LEAP_IMAGE_PROPERTIES* properties = &imageEvent->image[0].properties;
30  if (properties->bpp != 1)
31  return;
32 
33  if (properties->width*properties->height != image_size) {
34  void* prev_image_buffer = image_buffer;
35  image_width = properties->width;
36  image_height = properties->height;
37  image_size = image_width*image_height;
38  image_buffer = malloc(2 * image_size);
39  if (prev_image_buffer)
40  free(prev_image_buffer);
41  textureChanged = true;
42  }
43 
44  memcpy(image_buffer, (char*)imageEvent->image[0].data + imageEvent->image[0].offset, image_size);
45  memcpy((char*)image_buffer + image_size, (char*)imageEvent->image[1].data + imageEvent->image[1].offset, image_size);
46  imageReady = true;
47 }
48 
49 // Draw a textured quad displaying the image data
50 static void DrawImageQuad(float p1X, float p1Y, float p2X, float p2Y, int width, int height, void* imagedata){
51  glEnable(GL_TEXTURE_2D);
52  if(textureChanged){
53  textureChanged = false;
54  glDeleteTextures(1, &texture);
55  glGenTextures(1, &texture);
56  glBindTexture(GL_TEXTURE_2D, texture);
57  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
58  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, imagedata);
59  checkGLError("Initializing texture.");
60  } else { //update existing texture
61  glBindTexture ( GL_TEXTURE_2D, texture);
62  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, imagedata);
63  checkGLError("Updating texture.");
64  }
65  //Draw a texture-mapped quad
66  glBegin(GL_QUADS);
67  glTexCoord2f(1, 1); glVertex2f((GLfloat)p1X, (GLfloat)p1Y);
68  glTexCoord2f(0, 1); glVertex2f((GLfloat)p2X, (GLfloat)p1Y);
69  glTexCoord2f(0, 0); glVertex2f((GLfloat)p2X, (GLfloat)p2Y);
70  glTexCoord2f(1, 0); glVertex2f((GLfloat)p1X, (GLfloat)p2Y);
71  glEnd();
72  checkGLError("Drawing quad.");
73  glDisable(GL_TEXTURE_2D);
74 }
75 // Done drawing quad
76 
77 static void display(void)
78 {
79  glMatrixMode(GL_MODELVIEW);
80  glPushMatrix();
81  glTranslatef(-32, -24, -50); //"Camera" viewpoint
82  glClear(GL_COLOR_BUFFER_BIT);
83  if(imageReady){
84  DrawImageQuad(0, 0, 64, 48, image_width, image_height * 2, image_buffer);
85  imageReady = false;
86  }
87  glFlush();
88  glPopMatrix();
89  glutSwapBuffers();
90 }
91 
92 static void reshape(int w, int h)
93 {
94  glViewport(0, 0, (GLsizei) w, (GLsizei) h);
95  glMatrixMode(GL_PROJECTION);
96  glLoadIdentity();
97  gluPerspective(60, 640/240, 1.0, 1000);
98 }
99 
100 static void keyboard(unsigned char key, int x, int y)
101 {
102  switch((char)key) {
103  case 'q':
104  case 27: // ESC
105  glutDestroyWindow(window);
106  CloseConnection();
107  if(image_buffer) free(image_buffer);
108  exit(0);
109  default:
110  break;
111  }
112 }
113 
114 static void idle(void){
115  if(imageReady)
116  glutPostRedisplay();
117 }
118 
119 int main(int argc, char *argv[])
120 {
121  ConnectionCallbacks.on_image = OnImage;
122 
123  LEAP_CONNECTION *connection = OpenConnection();
124  LeapSetPolicyFlags(*connection, eLeapPolicyFlag_Images, 0);
125 
126  while(!IsConnected){
127  millisleep(250);
128  }
129 
130  glutInit(&argc, argv);
131  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
132  glutInitWindowSize(640, 480);
133  window = glutCreateWindow("LeapC Image Example");
134 
135  // GLUT callbacks
136  glutIdleFunc(idle);
137  glutReshapeFunc(reshape);
138  glutKeyboardFunc(keyboard);
139  glutDisplayFunc(display);
140 
141  // init GL
142  glClearColor(0.0, 0.0, 0.0, 0.0);
143  glColor3f(1.0, 1.0, 1.0);
144 
145  // Start GLUT loop
146  glutMainLoop();
147 
148  CloseConnection();
149  return 0;
150 }
151 //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.