kopia lustrzana https://github.com/FreeSpacenav/libspnav
401 wiersze
9.8 KiB
C
401 wiersze
9.8 KiB
C
/* This example demonstrates how to use 6-dof input for controlling the camera
|
|
* to fly around a scene. The native spacenav protocol is used, to also
|
|
* demonstrate how to integrate input from libspnav in an event loop.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/select.h>
|
|
#include <X11/Xlib.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include <GL/glx.h>
|
|
#include <spnav.h>
|
|
#include "xwin.h"
|
|
|
|
#ifndef GL_VERSION_1_1
|
|
#define glGenTextures glGenTexturesEXT
|
|
#define glDeleteTextures glDeleteTexturesEXT
|
|
#define glBindTexture glBindTextureEXT
|
|
#endif
|
|
|
|
#define GRID_REP 60
|
|
#define GRID_SZ 200
|
|
#define GRID_RES 7
|
|
|
|
void gen_textures(void);
|
|
void gen_scene(void);
|
|
void redraw(void);
|
|
void draw_scene(void);
|
|
void handle_spnav_event(spnav_event *ev);
|
|
int handle_xevent(XEvent *xev);
|
|
void draw_box(float xsz, float ysz, float zsz);
|
|
|
|
/* XXX: posrot contains a position vector and an orientation quaternion, and
|
|
* can be used with the spnav_posrot_moveview function to accumulate input
|
|
* motions, and then with spnav_matrix_view to create a view matrix.
|
|
* See util.c in the libspnav source code for implementation details.
|
|
*/
|
|
struct spnav_posrot posrot;
|
|
|
|
unsigned int grid_tex, box_tex;
|
|
unsigned int scene, skydome;
|
|
|
|
float apex_color[] = {0.07, 0.1, 0.4, 1.0f};
|
|
float horiz_color[] = {0.5, 0.2, 0.05, 1.0f};
|
|
|
|
|
|
int main(void)
|
|
{
|
|
int xsock, ssock, maxfd;
|
|
|
|
if(!(dpy = XOpenDisplay(0))) {
|
|
fprintf(stderr, "failed to connect to the X server");
|
|
return 1;
|
|
}
|
|
|
|
if(create_xwin("libspnav fly", 1024, 768) == -1) {
|
|
return 1;
|
|
}
|
|
|
|
/* XXX: open connection to the spacenav driver */
|
|
if(spnav_open() == -1) {
|
|
fprintf(stderr, "failed to connect to the spacenav driver\n");
|
|
return 1;
|
|
}
|
|
/* XXX: initialize the position vector & orientation quaternion */
|
|
spnav_posrot_init(&posrot);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glFogi(GL_FOG_MODE, GL_LINEAR);
|
|
glFogf(GL_FOG_START, GRID_SZ / 4.0f);
|
|
glFogf(GL_FOG_END, GRID_SZ);
|
|
glFogfv(GL_FOG_COLOR, horiz_color);
|
|
|
|
gen_textures();
|
|
gen_scene();
|
|
|
|
/* XXX: grab the Xlib socket and the libspnav socket. we'll need them in the
|
|
* select loop to wait for input from either source.
|
|
*/
|
|
xsock = ConnectionNumber(dpy); /* Xlib socket */
|
|
ssock = spnav_fd(); /* libspnav socket */
|
|
maxfd = xsock > ssock ? xsock : ssock;
|
|
|
|
for(;;) {
|
|
fd_set rdset;
|
|
|
|
/* XXX: add both sockets to the file descriptor set, to monitor both */
|
|
FD_ZERO(&rdset);
|
|
FD_SET(xsock, &rdset);
|
|
FD_SET(ssock, &rdset);
|
|
|
|
while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
|
|
|
|
/* XXX: handle any pending X events */
|
|
if(FD_ISSET(xsock, &rdset)) {
|
|
while(XPending(dpy)) {
|
|
XEvent xev;
|
|
XNextEvent(dpy, &xev);
|
|
|
|
if(handle_xevent(&xev) != 0) {
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* XXX: handle any pending spacenav events */
|
|
if(FD_ISSET(ssock, &rdset)) {
|
|
spnav_event sev;
|
|
while(spnav_poll_event(&sev)) {
|
|
handle_spnav_event(&sev);
|
|
}
|
|
}
|
|
|
|
if(redisplay_pending) {
|
|
redisplay_pending = 0;
|
|
redraw();
|
|
}
|
|
}
|
|
|
|
end:
|
|
glDeleteTextures(1, &grid_tex);
|
|
destroy_xwin();
|
|
spnav_close();
|
|
return 0;
|
|
}
|
|
|
|
void gen_textures(void)
|
|
{
|
|
int i, j, r, g, b;
|
|
static unsigned char pixels[128 * 128 * 3];
|
|
unsigned char *pptr;
|
|
|
|
pptr = pixels;
|
|
for(i=0; i<128; i++) {
|
|
float dy = abs(i - 64) / 64.0f;
|
|
for(j=0; j<128; j++) {
|
|
float dx = abs(j - 64) / 64.0f;
|
|
float d = dx > dy ? dx : dy;
|
|
float val = pow(d * 1.04f, 10.0) * 1.4f;
|
|
|
|
r = (int)(214.0f * val);
|
|
g = (int)(76.0f * val);
|
|
b = (int)(255.0f * val);
|
|
|
|
*pptr++ = r > 255 ? 255 : r;
|
|
*pptr++ = g > 255 ? 255 : g;
|
|
*pptr++ = b > 255 ? 255 : b;
|
|
}
|
|
}
|
|
|
|
glGenTextures(1, &grid_tex);
|
|
glBindTexture(GL_TEXTURE_2D, grid_tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 128, 128, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
pptr = pixels;
|
|
for(i=0; i<128; i++) {
|
|
int row = i >> 5;
|
|
float dy = ((i - 12) & 0x1f) / 16.0f;
|
|
for(j=0; j<128; j++) {
|
|
int col = j >> 5;
|
|
float dx = ((j - 12) & 0x1f) / 16.0f;
|
|
float d = dx > dy ? dx : dy;
|
|
int xor = (col ^ row) & 0xf;
|
|
|
|
r = d < 0.5f ? (xor << 4) + 20 : 0;
|
|
g = d < 0.5f ? (xor << 4) + 20 : 0;
|
|
b = d < 0.5f ? (xor << 4) + 20 : 0;
|
|
|
|
*pptr++ = r > 255 ? 255 : r;
|
|
*pptr++ = g > 255 ? 255 : g;
|
|
*pptr++ = b > 255 ? 255 : b;
|
|
}
|
|
}
|
|
|
|
glGenTextures(1, &box_tex);
|
|
glBindTexture(GL_TEXTURE_2D, box_tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 128, 128, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
|
}
|
|
|
|
void gen_scene(void)
|
|
{
|
|
int i, j, k;
|
|
float x, y, h, u, v, du;
|
|
|
|
srand(0);
|
|
|
|
scene = glGenLists(1);
|
|
glNewList(scene, GL_COMPILE);
|
|
|
|
/* grid */
|
|
glBindTexture(GL_TEXTURE_2D, grid_tex);
|
|
glBegin(GL_QUADS);
|
|
glColor3f(1, 1, 1);
|
|
|
|
du = 1.0f / (float)GRID_RES;
|
|
for(i=0; i<GRID_RES; i++) {
|
|
u = (float)i * du;
|
|
for(j=0; j<GRID_RES; j++) {
|
|
v = (float)j * du;
|
|
for(k=0; k<4; k++) {
|
|
int gc = k ^ (k >> 1);
|
|
float tu = (u + (gc & 1) * du) * GRID_REP;
|
|
float tv = (v + (gc >> 1) * du) * GRID_REP;
|
|
x = ((u - 0.5f) + (gc & 1) * du) * GRID_SZ * 2.0f;
|
|
y = ((v - 0.5f) + (gc >> 1) * du) * GRID_SZ * 2.0f;
|
|
glTexCoord2f(tu, tv);
|
|
glVertex3f(x, 0, -y);
|
|
}
|
|
}
|
|
}
|
|
glEnd();
|
|
|
|
/* buildings */
|
|
glBindTexture(GL_TEXTURE_2D, box_tex);
|
|
for(i=0; i<8; i++) {
|
|
for(j=0; j<8; j++) {
|
|
x = (j - 4.0f + 0.5f * (float)rand() / RAND_MAX) * 20.0f;
|
|
y = (i - 4.0f + 0.5f * (float)rand() / RAND_MAX) * 20.0f;
|
|
h = (3.0f + (float)rand() / RAND_MAX) * 6.0f;
|
|
|
|
glPushMatrix();
|
|
glTranslatef(x, h/2, y);
|
|
glMatrixMode(GL_TEXTURE);
|
|
glLoadIdentity();
|
|
glScalef(3, h/4, 1);
|
|
|
|
draw_box(6, h, 6);
|
|
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
glEndList();
|
|
|
|
skydome = glGenLists(1);
|
|
glNewList(skydome, GL_COMPILE);
|
|
|
|
/* skydome */
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
glColor3fv(apex_color);
|
|
glVertex3f(0, GRID_SZ / 5.0f, 0);
|
|
glColor3fv(horiz_color);
|
|
glVertex3f(-GRID_SZ, 0, -GRID_SZ);
|
|
glVertex3f(GRID_SZ, 0, -GRID_SZ);
|
|
glVertex3f(GRID_SZ, 0, GRID_SZ);
|
|
glVertex3f(-GRID_SZ, 0, GRID_SZ);
|
|
glVertex3f(-GRID_SZ, 0, -GRID_SZ);
|
|
glEnd();
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
glColor3fv(horiz_color);
|
|
glVertex3f(0, -GRID_SZ / 5.0f, 0);
|
|
glVertex3f(-GRID_SZ, 0, -GRID_SZ);
|
|
glVertex3f(-GRID_SZ, 0, GRID_SZ);
|
|
glVertex3f(GRID_SZ, 0, GRID_SZ);
|
|
glVertex3f(GRID_SZ, 0, -GRID_SZ);
|
|
glVertex3f(-GRID_SZ, 0, -GRID_SZ);
|
|
glEnd();
|
|
|
|
glEndList();
|
|
}
|
|
|
|
void redraw(void)
|
|
{
|
|
float xform[16];
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
/* XXX convert the accumulated position/rotation into a 4x4 view matrix */
|
|
spnav_matrix_view(xform, &posrot);
|
|
glMultMatrixf(xform); /* concatenate our computed view matrix */
|
|
glTranslatef(0, -5, 0); /* move the default view a bit higher above the ground */
|
|
|
|
glPushMatrix();
|
|
xform[12] = xform[13] = xform[14] = 0.0f;
|
|
glLoadMatrixf(xform);
|
|
glTranslatef(0, -5, 0);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glCallList(skydome);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glPopMatrix();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_FOG);
|
|
glCallList(scene);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_FOG);
|
|
|
|
glXSwapBuffers(dpy, win);
|
|
}
|
|
|
|
void reshape(int x, int y)
|
|
{
|
|
glViewport(0, 0, x, y);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
|
|
}
|
|
|
|
void handle_spnav_event(spnav_event *ev)
|
|
{
|
|
switch(ev->type) {
|
|
case SPNAV_EVENT_MOTION:
|
|
/* XXX use the spnav_posrot_moveview utility function to modify the view
|
|
* position vector and orientation quaternion, based on the motion input
|
|
* we received.
|
|
*
|
|
* We'll also divide rotation values by two, to make rotation less
|
|
* sensitive. In this scale, it feels better that way in fly mode. The
|
|
* ideal ratio of sensitivities will vary depending on the scene scale.
|
|
*/
|
|
ev->motion.rx /= 2;
|
|
ev->motion.ry /= 2;
|
|
ev->motion.rz /= 2;
|
|
spnav_posrot_moveview(&posrot, &ev->motion);
|
|
|
|
/* XXX: Drop any further pending motion events. This can make our input
|
|
* more responsive on slow or heavily loaded machines. We don't gain
|
|
* anything by processing a whole queue of relative motions.
|
|
*/
|
|
spnav_remove_events(SPNAV_EVENT_MOTION);
|
|
redisplay_pending = 1;
|
|
break;
|
|
|
|
case SPNAV_EVENT_BUTTON:
|
|
/* XXX reset position and orientation to identity on button presses to
|
|
* reset the view
|
|
*/
|
|
spnav_posrot_init(&posrot);
|
|
redisplay_pending = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void draw_box(float xsz, float ysz, float zsz)
|
|
{
|
|
xsz /= 2;
|
|
ysz /= 2;
|
|
zsz /= 2;
|
|
|
|
glBegin(GL_QUADS);
|
|
/* face +Z */
|
|
glNormal3f(0, 0, 1);
|
|
glTexCoord2f(0, 0); glVertex3f(-xsz, -ysz, zsz);
|
|
glTexCoord2f(1, 0); glVertex3f(xsz, -ysz, zsz);
|
|
glTexCoord2f(1, 1); glVertex3f(xsz, ysz, zsz);
|
|
glTexCoord2f(0, 1); glVertex3f(-xsz, ysz, zsz);
|
|
/* face +X */
|
|
glNormal3f(1, 0, 0);
|
|
glTexCoord2f(0, 0); glVertex3f(xsz, -ysz, zsz);
|
|
glTexCoord2f(1, 0); glVertex3f(xsz, -ysz, -zsz);
|
|
glTexCoord2f(1, 1); glVertex3f(xsz, ysz, -zsz);
|
|
glTexCoord2f(0, 1); glVertex3f(xsz, ysz, zsz);
|
|
/* face -Z */
|
|
glNormal3f(0, 0, -1);
|
|
glTexCoord2f(0, 0); glVertex3f(xsz, -ysz, -zsz);
|
|
glTexCoord2f(1, 0); glVertex3f(-xsz, -ysz, -zsz);
|
|
glTexCoord2f(1, 1); glVertex3f(-xsz, ysz, -zsz);
|
|
glTexCoord2f(0, 1); glVertex3f(xsz, ysz, -zsz);
|
|
/* face -X */
|
|
glNormal3f(-1, 0, 0);
|
|
glTexCoord2f(0, 0); glVertex3f(-xsz, -ysz, -zsz);
|
|
glTexCoord2f(1, 0); glVertex3f(-xsz, -ysz, zsz);
|
|
glTexCoord2f(1, 1); glVertex3f(-xsz, ysz, zsz);
|
|
glTexCoord2f(0, 1); glVertex3f(-xsz, ysz, -zsz);
|
|
/* face +Y */
|
|
glNormal3f(0, 1, 0);
|
|
glTexCoord2f(0, 0);
|
|
glVertex3f(-xsz, ysz, zsz);
|
|
glVertex3f(xsz, ysz, zsz);
|
|
glVertex3f(xsz, ysz, -zsz);
|
|
glVertex3f(-xsz, ysz, -zsz);
|
|
/* face -Y */
|
|
glNormal3f(0, -1, 0);
|
|
glTexCoord2f(0, 0);
|
|
glVertex3f(-xsz, -ysz, -zsz);
|
|
glVertex3f(xsz, -ysz, -zsz);
|
|
glVertex3f(xsz, -ysz, zsz);
|
|
glVertex3f(-xsz, -ysz, zsz);
|
|
glEnd();
|
|
}
|