}
if(use_sball && sball_pending()) {
sball_event ev;
- printf("got sball event\n");
while(sball_getevent(&ev)) {
handle_sball_event(&ev);
}
#include "screen.h"
#include "util.h"
#include "cfgopt.h"
+#include "mesh.h"
#ifdef MSDOS
void wait_vsync(void);
#endif
-#define NSPAWNPOS 128
+#define NSPAWNPOS 64
#define SPAWN_RATE 16.0f
-#define GRAV (-6.0f)
-#define HAIR_LENGTH 32
+//#define GRAV (-6.0f)
+#define GRAV 0.0f
+#define HAIR_LENGTH 20
struct particle {
vec3_t pos;
static long start_time;
-static vec3_t *spawnpos, *spawndir;
+static float cam_theta, cam_phi = 15;
+static float cam_dist = 8;
+static vec3_t *spawnpos, *spawndir;
static struct hairball hball;
+static struct g3d_mesh sphmesh;
struct screen *hairball_screen(void)
{
{
int i;
+ gen_sphere_mesh(&sphmesh, 1.0f, 12, 6);
+
hball.xform[0] = hball.xform[5] = hball.xform[10] = hball.xform[15] = 1.0f;
hball.rot.w = 1.0f;
prev_mx = mouse_x;
prev_my = mouse_y;
- if(mouse_bmask) {
- hball.pos.x += mouse_dx * 0.05;
- hball.pos.y -= mouse_dy * 0.05;
- }
- //hball.rot = quat_rotate(hball.rot, dt * 2.0f, 0, 1, 0);
+ if(opt.sball) {
+ memcpy(hball.xform, sball_matrix, 16 * sizeof(float));
+ } else {
+ if(mouse_bmask & MOUSE_BN_MIDDLE) {
+ hball.pos.x += mouse_dx * 0.05;
+ hball.pos.y -= mouse_dy * 0.05;
+ }
- quat_to_mat(hball.xform, hball.rot);
- hball.xform[12] = hball.pos.x;
- hball.xform[13] = hball.pos.y;
- hball.xform[14] = hball.pos.z;
+ quat_to_mat(hball.xform, hball.rot);
+ hball.xform[12] = hball.pos.x;
+ hball.xform[13] = hball.pos.y;
+ hball.xform[14] = hball.pos.z;
+ }
update_hairball(&hball, dt);
+
+ mouse_orbit_update(&cam_theta, &cam_phi, &cam_dist);
}
static void draw(void)
g3d_matrix_mode(G3D_MODELVIEW);
g3d_load_identity();
- g3d_translate(0, 0, -8);
+ g3d_translate(0, 0, -cam_dist);
+ g3d_rotate(cam_phi, 1, 0, 0);
+ g3d_rotate(cam_theta, 0, 1, 0);
draw_hairball(&hball);
static void draw_hairball(struct hairball *hb)
{
- int i, j, col;
+ int i, col;
struct particle *p;
vec3_t prevpos;
+ vec3_t start[NSPAWNPOS];
+
+ for(i=0; i<NSPAWNPOS; i++) {
+ start[i].x = hb->xform[0] * spawnpos[i].x + hb->xform[4] * spawnpos[i].y + hb->xform[8] * spawnpos[i].z + hb->xform[12];
+ start[i].y = hb->xform[1] * spawnpos[i].x + hb->xform[5] * spawnpos[i].y + hb->xform[9] * spawnpos[i].z + hb->xform[13];
+ start[i].z = hb->xform[2] * spawnpos[i].x + hb->xform[6] * spawnpos[i].y + hb->xform[10] * spawnpos[i].z + hb->xform[14];
+ }
g3d_begin(G3D_LINES);
for(i=0; i<NSPAWNPOS; i++) {
+ if(start[i].z >= hb->pos.z) continue;
p = hb->plist[i];
- prevpos.x = hb->xform[0] * spawnpos[i].x + hb->xform[4] * spawnpos[i].y + hb->xform[8] * spawnpos[i].z + hb->xform[12];
- prevpos.y = hb->xform[1] * spawnpos[i].x + hb->xform[5] * spawnpos[i].y + hb->xform[9] * spawnpos[i].z + hb->xform[13];
- prevpos.z = hb->xform[2] * spawnpos[i].x + hb->xform[6] * spawnpos[i].y + hb->xform[10] * spawnpos[i].z + hb->xform[14];
+ prevpos = start[i];
+ while(p) {
+ g3d_color3b(p->r, p->g, p->b);
+ g3d_vertex(prevpos.x, prevpos.y, prevpos.z);
+ g3d_vertex(p->pos.x, p->pos.y, p->pos.z);
+ prevpos = p->pos;
+ p = p->next;
+ }
+ }
+ g3d_end();
- j = 0;
+
+ g3d_push_matrix();
+ g3d_mult_matrix(hb->xform);
+ zsort_mesh(&sphmesh);
+ draw_mesh(&sphmesh);
+ g3d_pop_matrix();
+
+ g3d_begin(G3D_LINES);
+ for(i=0; i<NSPAWNPOS; i++) {
+ if(start[i].z < hb->pos.z) continue;
+ p = hb->plist[i];
+ prevpos = start[i];
while(p) {
g3d_color3b(p->r, p->g, p->b);
g3d_vertex(prevpos.x, prevpos.y, prevpos.z);
}
}
g3d_end();
+
}
}
}
+
+static void sphvec(float *res, float theta, float phi, float rad)
+{
+ theta = -theta;
+ res[0] = sin(theta) * sin(phi);
+ res[1] = cos(phi);
+ res[2] = cos(theta) * sin(phi);
+}
+
+int gen_sphere_mesh(struct g3d_mesh *mesh, float rad, int usub, int vsub)
+{
+ int i, j;
+ int nfaces, uverts, vverts;
+ struct g3d_vertex *vptr;
+ uint16_t *iptr;
+
+ mesh->prim = G3D_QUADS;
+
+ if(usub < 4) usub = 4;
+ if(vsub < 2) vsub = 2;
+
+ uverts = usub + 1;
+ vverts = vsub + 1;
+
+ mesh->vcount = uverts * vverts;
+ nfaces = usub * vsub;
+ mesh->icount = nfaces * 4;
+
+ if(!(mesh->varr = malloc(mesh->vcount * sizeof *mesh->varr))) {
+ fprintf(stderr, "gen_sphere_mesh: failed to allocate vertex buffer (%d vertices)\n", mesh->vcount);
+ return -1;
+ }
+ if(!(mesh->iarr = malloc(mesh->icount * sizeof *mesh->iarr))) {
+ fprintf(stderr, "gen_sphere_mesh: failed to allocate index buffer (%d indices)\n", mesh->icount);
+ return -1;
+ }
+ vptr = mesh->varr;
+ iptr = mesh->iarr;
+
+ for(i=0; i<uverts; i++) {
+ float u = (float)i / (float)(uverts - 1);
+ float theta = u * 2.0 * M_PI;
+
+ for(j=0; j<vverts; j++) {
+ float v = (float)j / (float)(vverts - 1);
+ float phi = v * M_PI;
+ int chess = (i & 1) == (j & 1);
+
+ sphvec(&vptr->x, theta, phi, rad);
+ vptr->w = 1.0f;
+
+ vptr->nx = vptr->x / rad;
+ vptr->ny = vptr->y / rad;
+ vptr->nz = vptr->z / rad;
+ vptr->u = u;
+ vptr->v = v;
+ vptr->r = chess ? 255 : 64;
+ vptr->g = 128;
+ vptr->b = chess ? 64 : 255;
+ ++vptr;
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+ *iptr++ = idx;
+ *iptr++ = idx + 1;
+ *iptr++ = idx + vverts + 1;
+ *iptr++ = idx + vverts;
+ }
+ }
+ }
+ return 0;
+}
+
int gen_plane_mesh(struct g3d_mesh *m, float width, float height, int usub, int vsub)
{
int i, j;
void normalize_mesh_normals(struct g3d_mesh *mesh);
+int gen_sphere_mesh(struct g3d_mesh *mesh, float rad, int usub, int vsub);
int gen_plane_mesh(struct g3d_mesh *mesh, float width, float height, int usub, int vsub);
int gen_cube_mesh(struct g3d_mesh *mesh, float sz, int sub);
int gen_torus_mesh(struct g3d_mesh *mesh, float rad, float ringrad, int usub, int vsub);
#include <stdio.h>
#include <stdlib.h>
+#include <math.h>
#include <limits.h>
#include <SDL/SDL.h>
#include "demo.h"
#include "tinyfps.h"
#include "timer.h"
#include "cfgopt.h"
+#include "sball.h"
+#include "vmath.h"
static void handle_event(SDL_Event *ev);
static void toggle_fullscreen(void);
+static int handle_sball_event(sball_event *ev);
+static void recalc_sball_matrix(float *xform);
+
+
static int quit;
static SDL_Surface *fbsurf;
static int xsz, ysz;
static unsigned int sdl_flags = SDL_SWSURFACE;
+static int use_sball;
+static vec3_t pos = {0, 0, 0};
+static quat_t rot = {0, 0, 0, 1};
+
+
int main(int argc, char **argv)
{
int s, i, j;
SDL_Quit();
return 1;
}
+
+ if(opt.sball && sball_init() == 0) {
+ use_sball = 1;
+ }
+
reset_timer();
while(!quit) {
if(quit) goto break_evloop;
}
+ if(use_sball) {
+ while(sball_pending()) {
+ sball_event ev;
+ sball_getevent(&ev);
+ handle_sball_event(&ev);
+ }
+ recalc_sball_matrix(sball_matrix);
+ }
+
time_msec = get_msec();
demo_draw();
drawFps(fb_pixels);
fbsurf = newsurf;
sdl_flags = newflags;
}
+
+
+
+#define TX(ev) ((ev)->motion.motion[0])
+#define TY(ev) ((ev)->motion.motion[1])
+#define TZ(ev) ((ev)->motion.motion[2])
+#define RX(ev) ((ev)->motion.motion[3])
+#define RY(ev) ((ev)->motion.motion[4])
+#define RZ(ev) ((ev)->motion.motion[5])
+
+static int handle_sball_event(sball_event *ev)
+{
+ switch(ev->type) {
+ case SBALL_EV_MOTION:
+ if(RX(ev) | RY(ev) | RZ(ev)) {
+ float rx = (float)RX(ev);
+ float ry = (float)RY(ev);
+ float rz = (float)RZ(ev);
+ float axis_len = sqrt(rx * rx + ry * ry + rz * rz);
+ if(axis_len > 0.0) {
+ rot = quat_rotate(rot, axis_len * 0.001, -rx / axis_len,
+ -ry / axis_len, -rz / axis_len);
+ }
+ }
+
+ pos.x += TX(ev) * 0.001;
+ pos.y += TY(ev) * 0.001;
+ pos.z += TZ(ev) * 0.001;
+ break;
+
+ case SBALL_EV_BUTTON:
+ if(ev->button.pressed) {
+ pos = v3_cons(0, 0, 0);
+ rot = quat_cons(1, 0, 0, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void recalc_sball_matrix(float *xform)
+{
+ quat_to_mat(xform, rot);
+ xform[12] = pos.x;
+ xform[13] = pos.y;
+ xform[14] = pos.z;
+}
--- /dev/null
+#include "sball.h"
+
+#ifdef __unix__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+
+#define SPNAV_SOCK_PATH "/var/run/spnav.sock"
+#define IS_OPEN (sock != -1)
+
+struct event_node {
+ sball_event event;
+ struct event_node *next;
+};
+
+/* only used for non-X mode, with spnav_remove_events */
+static struct event_node *ev_queue, *ev_queue_tail;
+
+/* AF_UNIX socket used for alternative communication with daemon */
+static int sock = -1;
+
+static int conn_unix(int s, const char *path)
+{
+ struct sockaddr_un addr;
+
+ memset(&addr, 0, sizeof addr);
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ return connect(s, (struct sockaddr*)&addr, sizeof addr);
+}
+
+
+int sball_init(void)
+{
+ int s;
+
+ if(IS_OPEN) {
+ return -1;
+ }
+
+ if(!(ev_queue = malloc(sizeof *ev_queue))) {
+ return -1;
+ }
+ ev_queue->next = 0;
+ ev_queue_tail = ev_queue;
+
+ if((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ }
+
+ if(conn_unix(s, SPNAV_SOCK_PATH) == -1) {
+ perror("failed to connect");
+ close(s);
+ return -1;
+ }
+
+ sock = s;
+ return 0;
+}
+
+void sball_shutdown(void)
+{
+ if(!IS_OPEN) {
+ return;
+ }
+
+ if(sock != -1) {
+ while(ev_queue) {
+ void *tmp = ev_queue;
+ ev_queue = ev_queue->next;
+ free(tmp);
+ }
+
+ close(sock);
+ sock = -1;
+ }
+}
+
+int sball_getdev(void)
+{
+ return sock;
+}
+
+/* Checks both the event queue and the daemon socket for pending events.
+ * In either case, it returns immediately with true/false values (doesn't block).
+ */
+int sball_pending(void)
+{
+ fd_set rd_set;
+ struct timeval tv;
+
+ if(ev_queue->next) {
+ return 1;
+ }
+
+ FD_ZERO(&rd_set);
+ FD_SET(sock, &rd_set);
+
+ /* don't block, just poll */
+ tv.tv_sec = tv.tv_usec = 0;
+
+ if(select(sock + 1, &rd_set, 0, 0, &tv) > 0) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/* If there are events waiting in the event queue, dequeue one and
+ * return that, otherwise read one from the daemon socket.
+ * This might block unless we called event_pending() first and it returned true.
+ */
+static int read_event(int s, sball_event *event)
+{
+ int i, rd;
+ int data[8];
+
+ /* if we have a queued event, deliver that one */
+ if(ev_queue->next) {
+ struct event_node *node = ev_queue->next;
+ ev_queue->next = ev_queue->next->next;
+
+ /* dequeued the last event, must update tail pointer */
+ if(ev_queue_tail == node) {
+ ev_queue_tail = ev_queue;
+ }
+
+ memcpy(event, &node->event, sizeof *event);
+ free(node);
+ return event->type;
+ }
+
+ /* otherwise read one from the connection */
+ do {
+ rd = read(s, data, sizeof data);
+ } while(rd == -1 && errno == EINTR);
+
+ if(rd <= 0) {
+ return 0;
+ }
+
+ if(data[0] < 0 || data[0] > 2) {
+ return 0;
+ }
+ event->type = data[0] ? SBALL_EV_BUTTON : SBALL_EV_MOTION;
+
+ if(event->type == SBALL_EV_MOTION) {
+ for(i=0; i<6; i++) {
+ event->motion.motion[i] = data[i + 1];
+ }
+ event->motion.motion[2] = -event->motion.motion[2];
+ event->motion.motion[5] = -event->motion.motion[5];
+ } else {
+ event->button.pressed = data[0] == 1 ? 1 : 0;
+ event->button.id = data[1];
+ }
+
+ return event->type;
+}
+
+
+int sball_getevent(sball_event *ev)
+{
+ if(sock) {
+ if(read_event(sock, ev) > 0) {
+ return ev->type;
+ }
+ }
+ return 0;
+}
+
+#else /* not __unix__ */
+
+int sball_init(void)
+{
+ return 0;
+}
+
+void sball_shutdown(void)
+{
+}
+
+int sball_getdev(void)
+{
+ return -1;
+}
+
+int sball_pending(void)
+{
+ return 0;
+}
+
+int sball_getevent(sball_event *ev)
+{
+ return SBALL_EV_NONE;
+}
+
+#endif