Skip to content

Commit f250cf1

Browse files
committed
- added new libspnav spinning cube example
git-svn-id: svn+ssh://svn.code.sf.net/p/spacenav/code/trunk/libspnav@80 ef983eb1-d774-4af8-acfd-baaf7b16a646
1 parent e02450d commit f250cf1

7 files changed

Lines changed: 454 additions & 2 deletions

File tree

examples/cube/Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
obj = cube.o vmath.o
2+
bin = cube
3+
4+
CC = gcc
5+
CFLAGS = -pedantic -Wall -g -I../..
6+
LDFLAGS = -L../.. -lX11 -lGL -lGLU -lm -lspnav
7+
8+
$(bin): $(obj)
9+
$(CC) -o $@ $(obj) $(LDFLAGS)
10+
11+
.PHONY: clean
12+
clean:
13+
rm -f $(obj) $(bin)

examples/cube/cube.c

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/* This example demonstrates how to use libspnav to get space navigator input,
2+
* and use that to rotate and translate a 3D cube. The magellan X11 protocol is
3+
* used (spnav_x11_open) which is compatible with both spacenavd and
4+
* 3Dconnexion's 3dxsrv.
5+
*
6+
* The code is a bit cluttered with X11 and GLX calls, so the interesting bits
7+
* are marked with XXX comments.
8+
*/
9+
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
#include <math.h>
13+
#include <X11/Xlib.h>
14+
#include <GL/gl.h>
15+
#include <GL/glu.h>
16+
#include <GL/glx.h>
17+
#include <spnav.h>
18+
#include "vmath.h"
19+
20+
#define SQ(x) ((x) * (x))
21+
22+
int create_gfx(int xsz, int ysz);
23+
void destroy_gfx(void);
24+
void set_window_title(const char *title);
25+
void redraw(void);
26+
void draw_cube(void);
27+
int handle_event(XEvent *xev);
28+
29+
30+
Display *dpy;
31+
Atom wm_prot, wm_del_win;
32+
GLXContext ctx;
33+
Window win;
34+
35+
vec3_t pos = {0, 0, -6};
36+
quat_t rot = {0, 0, 0, 1};
37+
38+
int redisplay;
39+
40+
int main(void)
41+
{
42+
if(!(dpy = XOpenDisplay(0))) {
43+
fprintf(stderr, "failed to connect to the X server");
44+
return 1;
45+
}
46+
47+
if(create_gfx(512, 512) == -1) {
48+
return 1;
49+
}
50+
51+
/* XXX: This actually registers our window with the driver for receiving
52+
* motion/button events through the 3dxsrv-compatible X11 protocol.
53+
*/
54+
if(spnav_x11_open(dpy, win) == -1) {
55+
fprintf(stderr, "failed to connect to the space navigator daemon\n");
56+
return 1;
57+
}
58+
59+
glEnable(GL_DEPTH_TEST);
60+
glEnable(GL_CULL_FACE);
61+
62+
for(;;) {
63+
XEvent xev;
64+
XNextEvent(dpy, &xev);
65+
66+
if(handle_event(&xev) != 0) {
67+
destroy_gfx();
68+
XCloseDisplay(dpy);
69+
return 0;
70+
}
71+
72+
if(redisplay) {
73+
redraw();
74+
redisplay = 0;
75+
}
76+
}
77+
return 0;
78+
}
79+
80+
int create_gfx(int xsz, int ysz)
81+
{
82+
int scr;
83+
Window root;
84+
XVisualInfo *vis;
85+
XSetWindowAttributes xattr;
86+
unsigned int events;
87+
XClassHint class_hint;
88+
89+
int attr[] = {
90+
GLX_RGBA, GLX_DOUBLEBUFFER,
91+
GLX_RED_SIZE, 8,
92+
GLX_GREEN_SIZE, 8,
93+
GLX_BLUE_SIZE, 8,
94+
GLX_DEPTH_SIZE, 24,
95+
None
96+
};
97+
98+
wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
99+
wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
100+
101+
scr = DefaultScreen(dpy);
102+
root = RootWindow(dpy, scr);
103+
104+
if(!(vis = glXChooseVisual(dpy, scr, attr))) {
105+
fprintf(stderr, "requested GLX visual is not available\n");
106+
return -1;
107+
}
108+
109+
if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
110+
fprintf(stderr, "failed to create GLX context\n");
111+
XFree(vis);
112+
return -1;
113+
}
114+
115+
xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
116+
xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
117+
118+
if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
119+
vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
120+
fprintf(stderr, "failed to create X window\n");
121+
return -1;
122+
}
123+
XFree(vis);
124+
125+
/* set the window event mask */
126+
events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
127+
ButtonReleaseMask | ButtonPressMask | PointerMotionMask;
128+
XSelectInput(dpy, win, events);
129+
130+
XSetWMProtocols(dpy, win, &wm_del_win, 1);
131+
132+
set_window_title("libspnav cube");
133+
134+
class_hint.res_name = "cube";
135+
class_hint.res_class = "cube";
136+
XSetClassHint(dpy, win, &class_hint);
137+
138+
if(glXMakeCurrent(dpy, win, ctx) == False) {
139+
fprintf(stderr, "glXMakeCurrent failed\n");
140+
glXDestroyContext(dpy, ctx);
141+
XDestroyWindow(dpy, win);
142+
return -1;
143+
}
144+
145+
XMapWindow(dpy, win);
146+
XFlush(dpy);
147+
148+
return 0;
149+
}
150+
151+
void destroy_gfx(void)
152+
{
153+
glXDestroyContext(dpy, ctx);
154+
XDestroyWindow(dpy, win);
155+
glXMakeCurrent(dpy, None, 0);
156+
}
157+
158+
void set_window_title(const char *title)
159+
{
160+
XTextProperty wm_name;
161+
162+
XStringListToTextProperty((char**)&title, 1, &wm_name);
163+
XSetWMName(dpy, win, &wm_name);
164+
XSetWMIconName(dpy, win, &wm_name);
165+
XFree(wm_name.value);
166+
}
167+
168+
void redraw(void)
169+
{
170+
mat4_t xform;
171+
172+
quat_to_mat(xform, rot);
173+
174+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
175+
176+
glMatrixMode(GL_MODELVIEW);
177+
glLoadIdentity();
178+
glTranslatef(pos.x, pos.y, pos.z);
179+
glMultTransposeMatrixf((float*)xform);
180+
181+
draw_cube();
182+
183+
glXSwapBuffers(dpy, win);
184+
}
185+
186+
void draw_cube(void)
187+
{
188+
glBegin(GL_QUADS);
189+
/* face +Z */
190+
glNormal3f(0, 0, 1);
191+
glColor3f(1, 0, 0);
192+
glVertex3f(-1, -1, 1);
193+
glVertex3f(1, -1, 1);
194+
glVertex3f(1, 1, 1);
195+
glVertex3f(-1, 1, 1);
196+
/* face +X */
197+
glNormal3f(1, 0, 0);
198+
glColor3f(0, 1, 0);
199+
glVertex3f(1, -1, 1);
200+
glVertex3f(1, -1, -1);
201+
glVertex3f(1, 1, -1);
202+
glVertex3f(1, 1, 1);
203+
/* face -Z */
204+
glNormal3f(0, 0, -1);
205+
glColor3f(0, 0, 1);
206+
glVertex3f(1, -1, -1);
207+
glVertex3f(-1, -1, -1);
208+
glVertex3f(-1, 1, -1);
209+
glVertex3f(1, 1, -1);
210+
/* face -X */
211+
glNormal3f(-1, 0, 0);
212+
glColor3f(1, 1, 0);
213+
glVertex3f(-1, -1, -1);
214+
glVertex3f(-1, -1, 1);
215+
glVertex3f(-1, 1, 1);
216+
glVertex3f(-1, 1, -1);
217+
/* face +Y */
218+
glNormal3f(0, 1, 0);
219+
glColor3f(0, 1, 1);
220+
glVertex3f(-1, 1, 1);
221+
glVertex3f(1, 1, 1);
222+
glVertex3f(1, 1, -1);
223+
glVertex3f(-1, 1, -1);
224+
/* face -Y */
225+
glNormal3f(0, -1, 0);
226+
glColor3f(1, 0, 1);
227+
glVertex3f(-1, -1, -1);
228+
glVertex3f(1, -1, -1);
229+
glVertex3f(1, -1, 1);
230+
glVertex3f(-1, -1, 1);
231+
glEnd();
232+
}
233+
234+
int handle_event(XEvent *xev)
235+
{
236+
static int win_mapped;
237+
KeySym sym;
238+
spnav_event spev;
239+
240+
switch(xev->type) {
241+
case MapNotify:
242+
win_mapped = 1;
243+
break;
244+
245+
case UnmapNotify:
246+
win_mapped = 0;
247+
break;
248+
249+
case Expose:
250+
if(win_mapped && xev->xexpose.count == 0) {
251+
redraw();
252+
}
253+
break;
254+
255+
case ClientMessage:
256+
/* XXX check if the event is a spacenav event */
257+
if(spnav_x11_event(xev, &spev)) {
258+
/* if so deal with motion and button events */
259+
if(spev.type == SPNAV_EVENT_MOTION) {
260+
/* apply axis/angle rotation to the quaternion */
261+
float angle = 0.000005 * sqrt(SQ(spev.motion.rx) + SQ(spev.motion.ry) + SQ(spev.motion.rz));
262+
rot = quat_rotate(rot, angle, -spev.motion.rx, -spev.motion.ry, spev.motion.rz);
263+
rot = quat_normalize(rot);
264+
265+
/* add translation */
266+
pos.x += spev.motion.x * 0.001;
267+
pos.y += spev.motion.y * 0.001;
268+
pos.z -= spev.motion.z * 0.001;
269+
270+
redisplay = 1;
271+
} else {
272+
/* on button press, reset the cube */
273+
if(spev.button.press) {
274+
pos = v3_cons(0, 0, -6);
275+
rot = quat_cons(1, 0, 0, 0);
276+
277+
redisplay = 1;
278+
}
279+
}
280+
/* finally remove any other queued motion events */
281+
spnav_remove_events(SPNAV_EVENT_MOTION);
282+
283+
} else if(xev->xclient.message_type == wm_prot) {
284+
if(xev->xclient.data.l[0] == wm_del_win) {
285+
return 1;
286+
}
287+
}
288+
break;
289+
290+
case KeyPress:
291+
sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
292+
if((sym & 0xff) == 27) {
293+
return 1;
294+
}
295+
296+
case ConfigureNotify:
297+
{
298+
int x = xev->xconfigure.width;
299+
int y = xev->xconfigure.height;
300+
301+
glMatrixMode(GL_PROJECTION);
302+
glLoadIdentity();
303+
gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
304+
305+
glViewport(0, 0, x, y);
306+
}
307+
break;
308+
309+
default:
310+
break;
311+
}
312+
return 0;
313+
}

examples/cube/vmath.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <math.h>
2+
#include "vmath.h"
3+
4+
quat_t quat_rotate(quat_t q, float angle, float x, float y, float z)
5+
{
6+
quat_t rq;
7+
float half_angle = angle * 0.5;
8+
float sin_half = sin(half_angle);
9+
10+
rq.w = cos(half_angle);
11+
rq.x = x * sin_half;
12+
rq.y = y * sin_half;
13+
rq.z = z * sin_half;
14+
15+
return quat_mul(q, rq);
16+
}

examples/cube/vmath.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef VMATH_H_
2+
#define VMATH_H_
3+
4+
typedef struct { float x, y, z; } vec3_t;
5+
typedef struct { float x, y, z, w; } vec4_t;
6+
7+
typedef vec4_t quat_t;
8+
9+
typedef float mat4_t[4][4];
10+
11+
/* vector functions */
12+
static inline vec3_t v3_cons(float x, float y, float z);
13+
static inline float v3_dot(vec3_t v1, vec3_t v2);
14+
15+
/* quaternion functions */
16+
static inline quat_t quat_cons(float s, float x, float y, float z);
17+
static inline vec3_t quat_vec(quat_t q);
18+
static inline quat_t quat_mul(quat_t q1, quat_t q2);
19+
static inline quat_t quat_normalize(quat_t q);
20+
static inline void quat_to_mat(mat4_t res, quat_t q);
21+
quat_t quat_rotate(quat_t q, float angle, float x, float y, float z);
22+
23+
/* matrix functions */
24+
static inline void m4_cons(mat4_t m,
25+
float m11, float m12, float m13, float m14,
26+
float m21, float m22, float m23, float m24,
27+
float m31, float m32, float m33, float m34,
28+
float m41, float m42, float m43, float m44);
29+
30+
#include "vmath.inl"
31+
32+
#endif /* VMATH_H_ */

0 commit comments

Comments
 (0)