kopia lustrzana https://github.com/FreeSpacenav/libspnav
- 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-baaf7b16a646pull/2/head
rodzic
e02450d6d5
commit
f250cf1598
|
@ -0,0 +1,13 @@
|
|||
obj = cube.o vmath.o
|
||||
bin = cube
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -pedantic -Wall -g -I../..
|
||||
LDFLAGS = -L../.. -lX11 -lGL -lGLU -lm -lspnav
|
||||
|
||||
$(bin): $(obj)
|
||||
$(CC) -o $@ $(obj) $(LDFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(obj) $(bin)
|
|
@ -0,0 +1,313 @@
|
|||
/* This example demonstrates how to use libspnav to get space navigator input,
|
||||
* and use that to rotate and translate a 3D cube. The magellan X11 protocol is
|
||||
* used (spnav_x11_open) which is compatible with both spacenavd and
|
||||
* 3Dconnexion's 3dxsrv.
|
||||
*
|
||||
* The code is a bit cluttered with X11 and GLX calls, so the interesting bits
|
||||
* are marked with XXX comments.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glx.h>
|
||||
#include <spnav.h>
|
||||
#include "vmath.h"
|
||||
|
||||
#define SQ(x) ((x) * (x))
|
||||
|
||||
int create_gfx(int xsz, int ysz);
|
||||
void destroy_gfx(void);
|
||||
void set_window_title(const char *title);
|
||||
void redraw(void);
|
||||
void draw_cube(void);
|
||||
int handle_event(XEvent *xev);
|
||||
|
||||
|
||||
Display *dpy;
|
||||
Atom wm_prot, wm_del_win;
|
||||
GLXContext ctx;
|
||||
Window win;
|
||||
|
||||
vec3_t pos = {0, 0, -6};
|
||||
quat_t rot = {0, 0, 0, 1};
|
||||
|
||||
int redisplay;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if(!(dpy = XOpenDisplay(0))) {
|
||||
fprintf(stderr, "failed to connect to the X server");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(create_gfx(512, 512) == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* XXX: This actually registers our window with the driver for receiving
|
||||
* motion/button events through the 3dxsrv-compatible X11 protocol.
|
||||
*/
|
||||
if(spnav_x11_open(dpy, win) == -1) {
|
||||
fprintf(stderr, "failed to connect to the space navigator daemon\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
for(;;) {
|
||||
XEvent xev;
|
||||
XNextEvent(dpy, &xev);
|
||||
|
||||
if(handle_event(&xev) != 0) {
|
||||
destroy_gfx();
|
||||
XCloseDisplay(dpy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(redisplay) {
|
||||
redraw();
|
||||
redisplay = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_gfx(int xsz, int ysz)
|
||||
{
|
||||
int scr;
|
||||
Window root;
|
||||
XVisualInfo *vis;
|
||||
XSetWindowAttributes xattr;
|
||||
unsigned int events;
|
||||
XClassHint class_hint;
|
||||
|
||||
int attr[] = {
|
||||
GLX_RGBA, GLX_DOUBLEBUFFER,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
GLX_DEPTH_SIZE, 24,
|
||||
None
|
||||
};
|
||||
|
||||
wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
||||
wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
||||
|
||||
scr = DefaultScreen(dpy);
|
||||
root = RootWindow(dpy, scr);
|
||||
|
||||
if(!(vis = glXChooseVisual(dpy, scr, attr))) {
|
||||
fprintf(stderr, "requested GLX visual is not available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
|
||||
fprintf(stderr, "failed to create GLX context\n");
|
||||
XFree(vis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
|
||||
xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
|
||||
|
||||
if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
|
||||
vis->visual, CWColormap | CWBackPixel | CWBorderPixel, &xattr))) {
|
||||
fprintf(stderr, "failed to create X window\n");
|
||||
return -1;
|
||||
}
|
||||
XFree(vis);
|
||||
|
||||
/* set the window event mask */
|
||||
events = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
|
||||
ButtonReleaseMask | ButtonPressMask | PointerMotionMask;
|
||||
XSelectInput(dpy, win, events);
|
||||
|
||||
XSetWMProtocols(dpy, win, &wm_del_win, 1);
|
||||
|
||||
set_window_title("libspnav cube");
|
||||
|
||||
class_hint.res_name = "cube";
|
||||
class_hint.res_class = "cube";
|
||||
XSetClassHint(dpy, win, &class_hint);
|
||||
|
||||
if(glXMakeCurrent(dpy, win, ctx) == False) {
|
||||
fprintf(stderr, "glXMakeCurrent failed\n");
|
||||
glXDestroyContext(dpy, ctx);
|
||||
XDestroyWindow(dpy, win);
|
||||
return -1;
|
||||
}
|
||||
|
||||
XMapWindow(dpy, win);
|
||||
XFlush(dpy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_gfx(void)
|
||||
{
|
||||
glXDestroyContext(dpy, ctx);
|
||||
XDestroyWindow(dpy, win);
|
||||
glXMakeCurrent(dpy, None, 0);
|
||||
}
|
||||
|
||||
void set_window_title(const char *title)
|
||||
{
|
||||
XTextProperty wm_name;
|
||||
|
||||
XStringListToTextProperty((char**)&title, 1, &wm_name);
|
||||
XSetWMName(dpy, win, &wm_name);
|
||||
XSetWMIconName(dpy, win, &wm_name);
|
||||
XFree(wm_name.value);
|
||||
}
|
||||
|
||||
void redraw(void)
|
||||
{
|
||||
mat4_t xform;
|
||||
|
||||
quat_to_mat(xform, rot);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(pos.x, pos.y, pos.z);
|
||||
glMultTransposeMatrixf((float*)xform);
|
||||
|
||||
draw_cube();
|
||||
|
||||
glXSwapBuffers(dpy, win);
|
||||
}
|
||||
|
||||
void draw_cube(void)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
/* face +Z */
|
||||
glNormal3f(0, 0, 1);
|
||||
glColor3f(1, 0, 0);
|
||||
glVertex3f(-1, -1, 1);
|
||||
glVertex3f(1, -1, 1);
|
||||
glVertex3f(1, 1, 1);
|
||||
glVertex3f(-1, 1, 1);
|
||||
/* face +X */
|
||||
glNormal3f(1, 0, 0);
|
||||
glColor3f(0, 1, 0);
|
||||
glVertex3f(1, -1, 1);
|
||||
glVertex3f(1, -1, -1);
|
||||
glVertex3f(1, 1, -1);
|
||||
glVertex3f(1, 1, 1);
|
||||
/* face -Z */
|
||||
glNormal3f(0, 0, -1);
|
||||
glColor3f(0, 0, 1);
|
||||
glVertex3f(1, -1, -1);
|
||||
glVertex3f(-1, -1, -1);
|
||||
glVertex3f(-1, 1, -1);
|
||||
glVertex3f(1, 1, -1);
|
||||
/* face -X */
|
||||
glNormal3f(-1, 0, 0);
|
||||
glColor3f(1, 1, 0);
|
||||
glVertex3f(-1, -1, -1);
|
||||
glVertex3f(-1, -1, 1);
|
||||
glVertex3f(-1, 1, 1);
|
||||
glVertex3f(-1, 1, -1);
|
||||
/* face +Y */
|
||||
glNormal3f(0, 1, 0);
|
||||
glColor3f(0, 1, 1);
|
||||
glVertex3f(-1, 1, 1);
|
||||
glVertex3f(1, 1, 1);
|
||||
glVertex3f(1, 1, -1);
|
||||
glVertex3f(-1, 1, -1);
|
||||
/* face -Y */
|
||||
glNormal3f(0, -1, 0);
|
||||
glColor3f(1, 0, 1);
|
||||
glVertex3f(-1, -1, -1);
|
||||
glVertex3f(1, -1, -1);
|
||||
glVertex3f(1, -1, 1);
|
||||
glVertex3f(-1, -1, 1);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
int handle_event(XEvent *xev)
|
||||
{
|
||||
static int win_mapped;
|
||||
KeySym sym;
|
||||
spnav_event spev;
|
||||
|
||||
switch(xev->type) {
|
||||
case MapNotify:
|
||||
win_mapped = 1;
|
||||
break;
|
||||
|
||||
case UnmapNotify:
|
||||
win_mapped = 0;
|
||||
break;
|
||||
|
||||
case Expose:
|
||||
if(win_mapped && xev->xexpose.count == 0) {
|
||||
redraw();
|
||||
}
|
||||
break;
|
||||
|
||||
case ClientMessage:
|
||||
/* XXX check if the event is a spacenav event */
|
||||
if(spnav_x11_event(xev, &spev)) {
|
||||
/* if so deal with motion and button events */
|
||||
if(spev.type == SPNAV_EVENT_MOTION) {
|
||||
/* apply axis/angle rotation to the quaternion */
|
||||
float angle = 0.000005 * sqrt(SQ(spev.motion.rx) + SQ(spev.motion.ry) + SQ(spev.motion.rz));
|
||||
rot = quat_rotate(rot, angle, -spev.motion.rx, -spev.motion.ry, spev.motion.rz);
|
||||
rot = quat_normalize(rot);
|
||||
|
||||
/* add translation */
|
||||
pos.x += spev.motion.x * 0.001;
|
||||
pos.y += spev.motion.y * 0.001;
|
||||
pos.z -= spev.motion.z * 0.001;
|
||||
|
||||
redisplay = 1;
|
||||
} else {
|
||||
/* on button press, reset the cube */
|
||||
if(spev.button.press) {
|
||||
pos = v3_cons(0, 0, -6);
|
||||
rot = quat_cons(1, 0, 0, 0);
|
||||
|
||||
redisplay = 1;
|
||||
}
|
||||
}
|
||||
/* finally remove any other queued motion events */
|
||||
spnav_remove_events(SPNAV_EVENT_MOTION);
|
||||
|
||||
} else if(xev->xclient.message_type == wm_prot) {
|
||||
if(xev->xclient.data.l[0] == wm_del_win) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
sym = XLookupKeysym((XKeyEvent*)&xev->xkey, 0);
|
||||
if((sym & 0xff) == 27) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
case ConfigureNotify:
|
||||
{
|
||||
int x = xev->xconfigure.width;
|
||||
int y = xev->xconfigure.height;
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(45.0, (float)x / (float)y, 1.0, 1000.0);
|
||||
|
||||
glViewport(0, 0, x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <math.h>
|
||||
#include "vmath.h"
|
||||
|
||||
quat_t quat_rotate(quat_t q, float angle, float x, float y, float z)
|
||||
{
|
||||
quat_t rq;
|
||||
float half_angle = angle * 0.5;
|
||||
float sin_half = sin(half_angle);
|
||||
|
||||
rq.w = cos(half_angle);
|
||||
rq.x = x * sin_half;
|
||||
rq.y = y * sin_half;
|
||||
rq.z = z * sin_half;
|
||||
|
||||
return quat_mul(q, rq);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef VMATH_H_
|
||||
#define VMATH_H_
|
||||
|
||||
typedef struct { float x, y, z; } vec3_t;
|
||||
typedef struct { float x, y, z, w; } vec4_t;
|
||||
|
||||
typedef vec4_t quat_t;
|
||||
|
||||
typedef float mat4_t[4][4];
|
||||
|
||||
/* vector functions */
|
||||
static inline vec3_t v3_cons(float x, float y, float z);
|
||||
static inline float v3_dot(vec3_t v1, vec3_t v2);
|
||||
|
||||
/* quaternion functions */
|
||||
static inline quat_t quat_cons(float s, float x, float y, float z);
|
||||
static inline vec3_t quat_vec(quat_t q);
|
||||
static inline quat_t quat_mul(quat_t q1, quat_t q2);
|
||||
static inline quat_t quat_normalize(quat_t q);
|
||||
static inline void quat_to_mat(mat4_t res, quat_t q);
|
||||
quat_t quat_rotate(quat_t q, float angle, float x, float y, float z);
|
||||
|
||||
/* matrix functions */
|
||||
static inline void m4_cons(mat4_t m,
|
||||
float m11, float m12, float m13, float m14,
|
||||
float m21, float m22, float m23, float m24,
|
||||
float m31, float m32, float m33, float m34,
|
||||
float m41, float m42, float m43, float m44);
|
||||
|
||||
#include "vmath.inl"
|
||||
|
||||
#endif /* VMATH_H_ */
|
|
@ -0,0 +1,78 @@
|
|||
/* vector functions */
|
||||
static inline vec3_t v3_cons(float x, float y, float z)
|
||||
{
|
||||
vec3_t res;
|
||||
res.x = x;
|
||||
res.y = y;
|
||||
res.z = z;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline vec3_t quat_vec(quat_t q)
|
||||
{
|
||||
vec3_t v;
|
||||
v.x = q.x;
|
||||
v.y = q.y;
|
||||
v.z = q.z;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline float v3_dot(vec3_t v1, vec3_t v2)
|
||||
{
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
/* quaternion functions */
|
||||
static inline quat_t quat_cons(float s, float x, float y, float z)
|
||||
{
|
||||
quat_t q;
|
||||
q.x = x;
|
||||
q.y = y;
|
||||
q.z = z;
|
||||
q.w = s;
|
||||
return q;
|
||||
}
|
||||
|
||||
static inline quat_t quat_mul(quat_t q1, quat_t q2)
|
||||
{
|
||||
quat_t res;
|
||||
vec3_t v1 = quat_vec(q1);
|
||||
vec3_t v2 = quat_vec(q2);
|
||||
|
||||
res.w = q1.w * q2.w - v3_dot(v1, v2);
|
||||
res.x = v2.x * q1.w + v1.x * q2.w + (v1.y * v2.z - v1.z * v2.y);
|
||||
res.y = v2.y * q1.w + v1.y * q2.w + (v1.z * v2.x - v1.x * v2.z);
|
||||
res.z = v2.z * q1.w + v1.z * q2.w + (v1.x * v2.y - v1.y * v2.x);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline quat_t quat_normalize(quat_t q)
|
||||
{
|
||||
float len = sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
||||
q.x /= len;
|
||||
q.y /= len;
|
||||
q.z /= len;
|
||||
q.w /= len;
|
||||
return q;
|
||||
}
|
||||
|
||||
static inline void quat_to_mat(mat4_t res, quat_t q)
|
||||
{
|
||||
m4_cons(res, 1.0 - 2.0 * q.y*q.y - 2.0 * q.z*q.z, 2.0 * q.x * q.y + 2.0 * q.w * q.z, 2.0 * q.z * q.x - 2.0 * q.w * q.y, 0,
|
||||
2.0 * q.x * q.y - 2.0 * q.w * q.z, 1.0 - 2.0 * q.x*q.x - 2.0 * q.z*q.z, 2.0 * q.y * q.z + 2.0 * q.w * q.x, 0,
|
||||
2.0 * q.z * q.x + 2.0 * q.w * q.y, 2.0 * q.y * q.z - 2.0 * q.w * q.x, 1.0 - 2.0 * q.x*q.x - 2.0 * q.y*q.y, 0,
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/* matrix functions */
|
||||
static inline void m4_cons(mat4_t m,
|
||||
float m11, float m12, float m13, float m14,
|
||||
float m21, float m22, float m23, float m24,
|
||||
float m31, float m32, float m33, float m34,
|
||||
float m41, float m42, float m43, float m44)
|
||||
{
|
||||
m[0][0] = m11; m[0][1] = m12; m[0][2] = m13; m[0][3] = m14;
|
||||
m[1][0] = m21; m[1][1] = m22; m[1][2] = m23; m[1][3] = m24;
|
||||
m[2][0] = m31; m[2][1] = m32; m[2][2] = m33; m[2][3] = m34;
|
||||
m[3][0] = m41; m[3][1] = m42; m[3][2] = m43; m[3][3] = m44;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
CC = gcc
|
||||
CFLAGS = -pedantic -Wall -g -I..
|
||||
LDFLAGS = -L.. -lspnav -lX11
|
||||
CFLAGS = -pedantic -Wall -g -I../..
|
||||
LDFLAGS = -L../.. -lspnav -lX11
|
||||
|
||||
.PHONY: all
|
||||
all: simple_x11 simple_af_unix
|
Ładowanie…
Reference in New Issue