/* * Syzygy-related functions for szgPartiview, including main program. */ static char local_id[] = "$Id$"; /* * $Log$ * Revision 1.12 2009/03/14 21:32:02 slevy * Initialize the cmdbuf transfer-buffer size to 1 byte. * szg gets mad if we try to select size 0. * * Revision 1.11 2008/07/28 09:22:58 slevy * Add CMDTEST hook for testing PpCmd packing/unpacking. Looks like it might work. * * Revision 1.10 2008/07/28 08:49:48 slevy * Scrap pp_ui_postinit(). It just overwrites settings that (a) were initialized * properly earlier and (b) might have been changed by command-line I/O. * I.e. let the "run" command work if included in a .cf file. * * Revision 1.9 2008/07/28 08:39:41 slevy * onPreExchange: Point sockp at a new bufferedSocket before passing to ar_accept()! * Tidy list insert/deletion. * Call clock_tick() periodically in master to allow time to pass. * onPostExchange: Add more error checking. * main: Hack to allow running under Linux+freeglut in standalone mode. * * Revision 1.8 2008/07/25 16:10:16 slevy * Lots of changes from Will Davis: new socket stuff * for listening for commands from network; * transfer fields; attempts to get "play" to play, etc. * Some from Stuart: PpCmd uses string object; * passing PpCmd commands by transfer-field * from master to slaves. * * Revision 1.7 2008/07/22 23:20:07 slevy * Need to resize() when adding new elements, not just reserve(). * * Revision 1.6 2008/07/22 18:46:14 slevy * Use ar_log_<whatever>() for msg() logging. Send to either _remark or _warning * according to whether msg() or warn() was called. * * Revision 1.5 2008/07/22 17:49:58 slevy * From Will Davis: globalize argc/argv for use in callback later. * onWindowStartGL() returns void. * * Implement PpCmd interface for serializing commands for network transport. * * Revision 1.4 2008/07/15 08:40:17 slevy * Don't mention "virtual" on *implementation* methods. * * Revision 1.3 2008/07/14 17:17:48 slevy * Attempt to glue in some arMasterSlaveFramework methods -- maybe enough to draw, * though not to process any commands after command-line startup yet. * */ #include "arPrecompiled.h" #include "arMasterSlaveFramework.h" #include <stdio.h> #include <stdlib.h> #include "config.h" #ifdef AR_USE_WIN_32 # include "winjunk.h" #endif #include "plugins.h" #include "specks.h" #include "szgPartiview.h" #include "shmem.h" #include <string.h> #include <stdarg.h> #include <ctype.h> #include <math.h> #include <errno.h> #include "partiviewc.h" #include "findfile.h" struct Ppszg ppszg; static int specks_commandstr( struct stuff **stp, const char *str ) { int result; if(stp == NULL || *stp == NULL || str == NULL) return 0; #define MAXARGS 128 char *av[MAXARGS]; int ac; char *txt = (char *)malloc( strlen(str) + 1 ); char *s = txt; strcpy(txt, str); for(ac = 0; ac < MAXARGS-1; ac++) { av[ac] = strtok(s, " \t\n"); if(av[ac] == NULL) break; s = NULL; } av[ac] = NULL; result = specks_parse_args( stp, ac,av); free(txt); return result; } int specks_commandfmt( struct stuff **stp, const char *fmt, ... ) { char cmd[1024], *cp; int ok; va_list args; va_start(args, fmt); vsprintf(cmd, fmt, args); va_end(args); for(cp = cmd; isspace(*cp); cp++) ; if(*cp == '\0') return 1; // accept null command implicitly ok = specks_commandstr( stp, cmd ); if(ok) parti_redraw(); else msg("Unrecognized control command: %s", cmd); return ok; } /* * These two are a quick fix to using argc and argv[] in the * onStart() function. I don't believe you can pass parameters * into, but I could be wrong. * */ int globalArgc; char** globalArgv; /* =================================================================== */ int pp_read( struct stuff **, int argc, char *argv[], char *fromfname, void * ) { /* this module defines only one command at present ... */ if(!strcmp( argv[0], "subcam")) { if(argc >= 9) { parti_make_subcam( argv[1], argc-2, argv+2 ); } else { msg("subcam: expected name az el rol L R B T, not %s", rejoinargs( 1, argc, argv )); } return 1; } return 0; } void parti_lighting() { static GLuint lightlist[MAXDSPCTX]; int ctx = get_dsp_context(); /* this would come from Syzygy wall number, say */ int listmaking = 0; if((unsigned int)ctx < MAXDSPCTX) { if(lightlist[ctx] > 0) { glCallList( lightlist[ctx] ); return; } else { lightlist[ctx] = glGenLists( 1 ); glNewList( lightlist[ctx], GL_COMPILE_AND_EXECUTE ); listmaking = 1; } } else { listmaking = 0; } static GLfloat lmambient[] = { 0.1, 0.1, 0.1, 1.0 }; static GLfloat amblight[] = { 0.1, 0.1, 0.1, 1.0 }; static GLfloat whitelight[] = { 1, 1, 1, 1 }; static GLfloat Ce[] = { 0, 0, 0, 1 }; static GLfloat Ca[] = { 1, 1, 1, 1 }; static GLfloat Cd[] = { 1, 1, 1, 1 }; static GLfloat Cs[] = { 1, 1, 1, 1 }; static GLfloat lightpos[3][4] = { 0, 0, -1, 0, 1, 0, -.5, 0, 0, 1, 0, 0, }; glShadeModel( GL_SMOOTH ); glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE ); glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE ); glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmambient ); glDisable( GL_LIGHTING ); int i; for(i = 0; i < 8; i++) glDisable( GL_LIGHT0+i ); for(i = 0; i < 3; i++) { glLightfv( GL_LIGHT0+i, GL_AMBIENT, amblight ); glLightfv( GL_LIGHT0+i, GL_SPECULAR, whitelight ); glLightfv( GL_LIGHT0+i, GL_DIFFUSE, whitelight ); glLightfv( GL_LIGHT0+i, GL_POSITION, lightpos[i] ); glEnable( GL_LIGHT0+i ); } glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, Ce ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, Ca ); glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, Cd ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, Cs ); glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 32.0 ); glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE ); glDisable( GL_COLOR_MATERIAL ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glDisable( GL_BLEND ); if(listmaking) glEndList(); } PvObject::PvObject() : st_(0), name_(0), id_(-1), parent_(0) { } PvObject::PvObject( const char *name, int id ) { this->init( name, id ); } void PvObject::init( const char *name, int id ) { struct stuff *st = specks_init( 0, NULL ); st->clk = ppszg.clk; this->st_ = st; this->To2w( &Tidentity ); this->name_ = name ? strdup(name) : strdup(""); this->id_ = id; this->parent_ = 0; } void PvObject::draw( bool inpick ) { if(st_ == NULL) return; specks_set_timestep( st_ ); specks_current_frame( st_, st_->sl ); glPushMatrix(); glMultMatrixf( & To2w_.m[0] ); st_->inpick = (int)inpick; drawspecks( st_ ); st_->inpick = 0; glPopMatrix(); } void drawetc( const Point *cen, float censize ) { static Point xyz[3] = {{{1,0,0}}, {{0,1,0}}, {{0,0,1}}}; static Point nxyz[3] = {{{-1,0,0}}, {{0,-1,0}}, {{0,0,-1}}}; static Point zero = {{0,0,0}}; static float white[3] = {1,1,1}; int i; // if(ppszg.drawtrace) // (*ppszg.drawtrace)(); parti_lighting(); glDisable(GL_LIGHTING); if(censize > 0) { glPushMatrix(); glScalef(censize,censize,censize); glBegin(GL_LINES); for(i = 0; i < 3; i++) { glColor3fv(xyz[i].x); glVertex3fv(xyz[i].x); glVertex3f(0,0,0); } glEnd(); glPopMatrix(); } if(censize != 0) { censize = fabs(censize); glPushMatrix(); glTranslatef(cen->x[0],cen->x[1],cen->x[2]); glScalef(censize, censize, censize); glBegin(GL_LINES); for(i = 0; i < 3; i++) { glColor3fv(white); glVertex3fv(nxyz[i].x); glVertex3fv(zero.x); glColor3fv(xyz[i].x); glVertex3fv(zero.x); glVertex3fv(xyz[i].x); } glEnd(); glPopMatrix(); } } // Draw entire partiview scene. // XXX How can we handle background color, ppszg.bgcolor? Do we glClear() here ?? // // YYY setting "inpick" is part of what's needed to go into OpenGL "selection" mode, // YYY but we don't need that immediately if at all. // Just call draw() and let inpick default to false. void PvScene::draw( bool inpick ) { drawetc( center(), censize() ); for(int i = 0; i < nobjs(); i++) { obj(i)->draw( inpick ); } } PvObject *PvScene::addobj( const char *objname, int id ) { if(id < 0) id = objs_.size(); if(objstuff(id) == 0) { if(id >= objs_.capacity()) objs_.reserve( id + 15 ); if(id >= objs_.size()) objs_.resize( id+1 ); objs_[id].init( objname, id ); } else { msg("Warning: Reusing object g%d (named %s)", id, objname); } return &objs_[id]; } // watching file descriptors for external input, e.g. from network. // XXX good to implement these someday. void parti_asyncfd( int fd ) { msg("IGNORING parti_asyncfd(%d)", fd); } void parti_unasyncfd( int fd ) { // XXX } typedef enum { REMARK, WARN } MsgLevel; MsgLevel msglevel = REMARK; int vmsg( const char *fmt, va_list args ) { char str[10240]; #ifdef HAVE_SNPRINTF vsnprintf(str, sizeof(str), fmt, args); #else vsprintf(str, fmt, args); #endif // XXX Now do something with str! fputs(str, stderr); fputc('\n', stderr); if(msglevel == REMARK) ar_log_remark() << str; else ar_log_warning() << str; return 0; } int msg( const char *fmt, ... ) { int val; va_list args; msglevel = REMARK; va_start(args, fmt); val = vmsg( fmt, args ); va_end(args); return val; } void warn( const char *fmt, ... ) { va_list args; msglevel = WARN; va_start(args, fmt); vmsg(fmt, args); va_end(args); } // partiview <-> syzygy navigation. void copyfrom_arMatrix( Matrix *dstT, const arMatrix4 *srcarT ) { int i; for(i = 0; i < 16; i++) dstT->m[i] = srcarT->v[i]; } void copyto_arMatrix( arMatrix4 *dstarT, const Matrix *srcT ) { int i; for(i = 0; i < 16; i++) dstarT->v[i] = srcT->m[i]; } void parti_getc2w( Matrix *c2w ) { /* * Modified July 23, 2008 William Davis * We needed the non-inverted matrix, * changed from ar_getNavInvMatrix to * ar_getNavMatrix(); */ arMatrix4 navmat = ar_getNavMatrix(); copyfrom_arMatrix( c2w, &navmat ); } void parti_setc2w( const Matrix *c2w ) { //msg("IGNORING parti_setc2w() (\"jump\")"); //XXX handle the "jump" command. Something like /* * Modified July 23, 2008 William Davis * Sets the navigation matrix to the coordinates * specified by the "jump" command * Removed eucinv( &w2c, c2w ) * Modified copyto_arMatrix() to match */ arMatrix4 jumpto; copyto_arMatrix( &jumpto, c2w ); ar_setNavMatrix( ar_matrixFromNavCoords(jumpto) ); } double syzygy_time() { // called from sclock.c return ppszg.getTime() * 0.001; } // Camera Animation (from .wf path) static void playidle( void * ) { struct wfpath *path = &ppszg.path; if(!ppszg.playing) { // XXX disable calling playidle() in this case. // Fl::remove_idle( playidle, NULL ); ppszg.playidling = 0; return; } double now; #if TESTCAVE now = ppszg.getTime(); #else now = wallclock_time(); #endif if(ppszg.playspeed == 0) ppszg.playspeed = 1; int frame = ppszg.framebase; if(ppszg.playevery) { frame += (int) ((ppszg.playspeed < 0) /* round away from zero */ ? ppszg.playspeed - .999f : ppszg.playspeed + .999f); ppszg.framebase = frame; ppszg.playtimebase = now; } else { frame += (int) ((now - ppszg.playtimebase) * ppszg.playspeed * path->fps); } if(frame < path->frame0 || frame >= path->frame0 + path->nframes) { frame = (frame < path->frame0) ? path->frame0 : path->frame0 + path->nframes-1; switch(ppszg.playloop) { case 0: /* Not looping; just stop at end. */ // XXX disable calling playidle() here // Fl::remove_idle( playidle, NULL ); ppszg.playidling = 0; //parti_play( "stop" ); break; case -1: /* Bounce: stick at same end of range, change direction */ ppszg.playspeed = -ppszg.playspeed; break; case 1: /* Loop: jump to other end of range */ frame = (frame == path->frame0) ? path->frame0 + path->nframes-1 : path->frame0; } ppszg.framebase = frame; ppszg.playtimebase = now; parti_setframe( frame ); return; } /* * Cases: * - we're displaying frames slower than real time. * => use an idle function. * - we're displaying frames faster than real time. * => use a timeout. */ int needidle = ppszg.playidling; float dt = 0; if(frame != path->curframe) { parti_setframe( frame ); needidle = 1; } else { frame += (ppszg.playspeed > 0) ? 1 : -1; dt = (frame - ppszg.framebase) / (ppszg.playspeed * path->fps) + ppszg.playtimebase - now; needidle = (dt < .05); } if(needidle && !ppszg.playidling) { // XXX enable calling playidle() here // Fl::add_idle( playidle, NULL ); ppszg.playidling = 1; } if(!needidle && ppszg.playidling) { // XXX disable calling playidle() here // Fl::remove_idle( playidle, NULL ); ppszg.playidling = 0; } ppszg.playidling = needidle; if(!needidle) { // XXX schedule calling playidle once, "dt" seconds from now // Fl::add_timeout( dt, playidle, NULL ); } } void parti_play( const char *rate ) { struct wfpath *path = &ppszg.path; int playnow = 1; int awaitdone = 0; msg("Inside the parti_play!"); msg(rate); if(rate != NULL) { char *ep; float sp = strtod(rate, &ep); printf("Rate: %f\n", sp); if(strchr(ep, 's')) playnow = 0; if(strchr(ep, 'l')) ppszg.playloop = 1; else if(strchr(ep, 'k')) ppszg.playloop = -1; /* rock */ else if(strchr(ep, 'f') || strchr(ep, 'e')) ppszg.playevery = 1; else if(strchr(ep, 'r') || strchr(ep, 't')) ppszg.playevery = 0; if(strstr(rate, "wait")) awaitdone = 1; if(ep == rate) { /* No leading number -- did we get just "-" or "-?" or "-help"? */ if(0==strcmp(rate, "-") || strchr(rate, '?') || strchr(rate, 'h')) { msg("Usage: play [rate][s][l|k][f|r][wait] e.g. \"play\" or \"play 30\" or \"play 10kf wait\""); msg(" rate frames/sec; [s]et speed, don't play now; [l]oop/roc[k]"); msg(" play every [f]rame/skip to maintain [r]ate; [wait] until done"); return; } if(sp == 0) playnow = 0; else ppszg.playspeed = sp; } } // Disable playidle() idle-function // Fl::remove_idle( playidle, NULL ); // Fl::remove_timeout( playidle, NULL ); ppszg.playing = 0; ppszg.playidling = 0; if(playnow) { #if CAVE ppszg.playtimebase = .001 * ppszg.getTime(); #else ppszg.playtimebase = wallclock_time(); #endif printf("playtimebase: %f", ppszg.playtimebase); ppszg.framebase = path->curframe; if(ppszg.playspeed > 0 && path->curframe >= path->frame0+path->nframes-1) ppszg.framebase = path->frame0; else if(ppszg.playspeed < 0 && path->curframe <= path->frame0) ppszg.framebase = path->frame0 + path->nframes - 1; parti_setframe( ppszg.framebase ); /* Switch into "play" mode */ // XXX start calling playidle() idle-function ppszg.playidling = 1; ppszg.playing = 1; } } // Idle function -- enabled when data-animation clock is running void pp_stepper() { if(ppszg.st) { clock_tick( ppszg.st->clk ); specks_set_timestep( ppszg.st ); } } // Data animation void parti_set_running( struct stuff *st, int on ) { if(clock_running(st->clk) != on) { if(on) { // XXX enable pp_stepper idle function } else { // XXX disable pp_stepper idle function, no longer needed (?) } if(st->clk) st->clk->walltimed = 1; clock_set_running(st->clk, on); } } void readrc( struct stuff **stp ) { char *rc = findfile( NULL, "~/.partiviewrc" ); if(rc) specks_read( stp, rc ); rc = findfile( NULL, "./.partiviewrc" ); if(rc) specks_read( stp, rc ); } static int cmdargs(int argc, char **argv, int & optind) { char *arg = argv[optind]; if(!strcmp("-c", arg)) { arg = argv[optind+1]; char *t = NewN(char, strlen(arg)+1); char *av[128]; int ac = tokenize(arg, t, 128, av, NULL); specks_parse_args( &ppszg.st, ac, av ); Free(t); optind += 2; return 1; } return 0; } int pp_parse_args( struct stuff **, int argc, char *argv[], char *fromfname, void * ) { if(argc < 1) return 0; int i; if(!strcmp( argv[0], "exit" )) { exit(0); } else if(!strcmp( argv[0], "stereo" )) { for(i = 1; i < argc; i++) parti_stereo( argv[i] ); msg("stereo %s", parti_stereo(NULL)); } else if(!strcmp( argv[0], "winsize" )) { msg("winsize %s", parti_winsize( rejoinargs( 1, argc, argv ) ) ); } else if(!strncmp( argv[0], "snapset", 7 ) || !strcmp( argv[0], "snapshot" )) { char *frameno = NULL, *basename = NULL; char *size = NULL; int now = (0 == strcmp(argv[0], "snapshot")); while(argc > 2) { if(!strcmp(argv[1], "-w")) { size = argv[2]; } else if(!strcmp(argv[1], "-n")) { frameno = argv[2]; } else break; argc -= 2, argv += 2; } if(argc > 1) basename = rejoinargs( 1, argc, argv ); parti_snapset( basename, frameno, size ); if(now) { char snapinfo[1024]; if(parti_snapshot(snapinfo) >= 0) msg("Snapped %s", snapinfo); } } else if(!strcmp( argv[0], "clip" )) { msg("%s", parti_clip( argv[1], argc>2?argv[2]:NULL ) ); } else if(!strcmp( argv[0], "pickrange" )) { float pickrange = parti_pickrange( argv[1] ); msg("pickrange %g", parti_pickrange( NULL )); } else if(!strcmp( argv[0], "fov" ) || !strcmp( argv[0], "fovy" )) { float fovy = parti_fovy( (argc>1) ? argv[1] : NULL ); msg("fovy %g", fovy); } else if(!strcmp( argv[0], "subcam")) { int index; if(argc > 1) { if(!strcmp(argv[1], "-") || !strcmp(argv[1], "off")) { parti_select_subcam(0); } else { index = parti_make_subcam( argv[1], argc-2, argv+2 ); if(index > 0) { parti_select_subcam(index); } else { char *what = parti_subcam_list(); msg(what[0]=='\0' ? "No subcams defined yet" : "Known subcams: %s", what); } } } index = parti_current_subcam(); if(index > 0) { char params[100]; char *name = parti_get_subcam( index, params ); msg("subcam %s %s # az el rol L R B T", name, params); } else { int wx, wy; float hx, hy = .5 * parti_fovy(NULL); sscanf( parti_winsize(NULL), "%d%*c%d", &wx, &wy ); if(wy == 0) wy = 1; hx = atan( tan(hy*(M_PI/180))*wx / wy ) * (180/M_PI); msg("subcam off 0 0 0 %g %g %g %g # a e r L R B T (default)", hx, hx, hy, hy); } } else if(!strncmp( argv[0], "focallen", 8 )) { float focallen = parti_focal( (argc>1) ? argv[1] : NULL ); msg("focallength %g", focallen); } else if(!strcmp( argv[0], "focalpoint" )) { /* args: * on/off * x y z [minfocallen] */ Point fpt; float minlen; int ison = parti_focalpoint( argc-1, (argv+1), &fpt, &minlen ); msg(ison ? "focalpoint %g %g %g %g # pointxyz minfocallen" : "focalpoint off # %g %g %g %g # pointxyz minfocallen", fpt.x[0],fpt.x[1],fpt.x[2], minlen); } else if(!strncmp( argv[0], "jump", 4 )) { Point xyz; float aer[3]; static int stupid[3] = {1,0,2}; /* aer -> rx ry rz */ Matrix c2w; parti_getc2w( &c2w ); tfm2xyzaer( &xyz, aer, &c2w ); if(argc>1) { for(i=1; i<argc && i<4+3; i++) { if(i-1<3) xyz.x[i-1] = getfloat(argv[i], xyz.x[i-1]); else aer[stupid[i-4]] = getfloat(argv[i], aer[stupid[i-4]]); } xyzaer2tfm( &c2w, &xyz, aer ); parti_setc2w( &c2w ); } msg("jump %g %g %g %g %g %g (XYZ RxRyRz)", xyz.x[0],xyz.x[1],xyz.x[2], aer[1],aer[0],aer[2]); } else if(!strncmp( argv[0], "home", 4 )) {//marx version 0.7.03 Point xyz; float aer[3]; static int stupid[3] = {1,0,2}; /* aer -> rx ry rz */ Matrix c2w; parti_getc2w( &c2w ); tfm2xyzaer( &xyz, aer, &c2w ); if(argc>1) { for(i=1; i<argc && i<4+3; i++) { if(i-1<3) xyz.x[i-1] = getfloat(argv[i], xyz.x[i-1]); else aer[stupid[i-4]] = getfloat(argv[i], aer[stupid[i-4]]); } xyzaer2tfm( &c2w, &xyz, aer ); parti_setc2w( &c2w ); ppszg.home[0] = xyz.x[0]; ppszg.home[1] = xyz.x[1]; ppszg.home[2] = xyz.x[2]; //ppszg.home[3] = aer[0]; ppszg.home[4] = aer[1]; ppszg.home[5] = aer[2]; bug in releases >= 0.7.03 ppszg.home[3] = aer[1]; ppszg.home[4] = aer[0]; ppszg.home[5] = aer[2]; //version 0.7.05 fix for the line above } msg("home %g %g %g %g %g %g (XYZ RxRyRz)", ppszg.home[0], ppszg.home[1], ppszg.home[2], ppszg.home[3], ppszg.home[4], ppszg.home[5]); } else if((!strcmp( argv[0], "rdata" ) || !strcmp(argv[0], "readpath")) && argc>1) { bool freet = false; char *tfname = argv[1]; char *realfile = findfile( fromfname, tfname ); if(realfile == NULL) { tfname = (char *)malloc(strlen(argv[1]) + 32); freet = true; sprintf(tfname, "data/record/%s%s", argv[1], strstr(argv[1], ".wf") ? "" : ".wf"); realfile = findfile( NULL, tfname+12 ); } if(realfile == NULL) realfile = findfile( fromfname, tfname ); if(realfile) parti_readpath( realfile ); else msg("%s: can't find \"%s\" nor data/record/... nor ...wf", argv[0],argv[1]); if(freet) free(tfname); } else if(!strcmp( argv[0], "play" )) { parti_play( argc>1 ? rejoinargs(1, argc, argv) : NULL ); } else if(!strcmp( argv[0], "frame" )) { CONST struct wfpath *pp; i = parti_frame( argv[1], &pp ); msg("frame %d (of %d..%d)", pp->curframe, pp->frame0, pp->nframes + pp->frame0 - 1); } else if(!strcmp( argv[0], "interest") || !strcmp( argv[0], "int" ) || !strcmp( argv[0], "center" ) || !strcmp( argv[0], "cen" )) { Point cen; float censize = parti_getcensize(); parti_getcenter( &cen ); if(argc > 1) { sscanf(argv[1], "%f%*c%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2], &censize); if(argc > 3) for(i=0;i<3;i++) cen.x[i] = getfloat(argv[i+1], cen.x[i]); if(argc == 3 || argc == 5) sscanf(argv[argc-1], "%f", &censize); parti_center( &cen ); if(censize != parti_getcensize()) parti_censize( censize ); } msg("center %g %g %g %g(radius)", cen.x[0],cen.x[1],cen.x[2], censize); } else if(!strcmp( argv[0], "censize" )) { if(argc>1) parti_censize( getfloat(argv[1], parti_getcensize())); msg("censize %g (interest-marker size)", parti_getcensize()); } else { return 0; } return 1; } void pp_ui_postinit() { parti_set_timebase( ppszg.st, 0.0 ); parti_set_timestep( ppszg.st, 0.0 ); parti_set_running( ppszg.st, 0 ); parti_set_fwd( ppszg.st, 1 ); } void clearBuffer(char* buf, int bufsize) { for(int i=0; i < bufsize; i++) { buf[i] = '\0'; } } PpCmd::PpCmd( CmdType type, int argc, char **argv ) { this->type = type; this->argc = argc; this->argv = argv; } PpCmd::~PpCmd() { } int PpCmd::frozenLen() const { int len = 2*argc+2; for(int i = 0; i < argc; i++) len += strlen(argv[i]); return len; } /* Serialize the command, and append result to 'buf' */ int PpCmd::freezeInto( string &buf ) const { int ineed = buf.size() + frozenLen() + 1; if(buf.capacity() < ineed) buf.reserve( 2*ineed+1 ); for(int i = 0; i < argc; i++) { buf.append( 1, (char)type ); buf.append( argv[i], strlen(argv[i])+1 ); } buf.append( 1, '\000' ); // mark end of last arg return buf.size(); } int PpCmd::thawFrom( char *buf, int &offset, bool copystrings ) { if(buf == 0) return 0; int p = offset; argc = 0; argv = 0; type = (CmdType) buf[p]; switch(type) { case CMD_DATA: case CMD_CONTROL: break; // OK. case CMD_NONE: offset = p; return -1; // EOF default: msg("PpCmd::thawFrom(): Trouble decoding %p at offset %d (got 0x%x not 0/1/2)", buf, p, buf[p]); type = CMD_NONE; return -1; } // count args do { p++; p += strlen( &buf[p] ) + 1; argc++; } while(buf[p] != 0); argv = new char *[argc+1]; // copy args p = offset; for(int a = 0; a < argc; a++) { p++; argv[a] = copystrings ? strdup(&buf[p]) : &buf[p]; p += strlen(&buf[p]) + 1; } argv[argc] = 0; offset = p+1; return argc; } Ppszg::~Ppszg() { delete scene; // delete clk; Don't delete the clock -- might be shared with other objects } Ppszg::Ppszg() { scene = new PvScene(); st = NULL; clipnear_ = 0.1; clipfar_ = 2500; focallen_ = 3; snapfmt = NULL; snapfno = 0; pickrange = 3.5; memset(home, 0, sizeof(home)); clk = new SClock(); clock_init( clk ); clock_set_speed( clk, 1.0 ); clk->continuous = 1; clk->walltimed = 1; memset( &path, 0, sizeof(path) ); playing = 0; playevery = 0; playidling = 0; playspeed = 20; framebase = 1; playtimebase = 0; playloop = 0; timebasetime = 0; subcam = 0; /* arSockets - originally from salimiman by Jim Crowell*/ /* I'm not sure if some of this should be done elsewhere */ listenSocket = new arSocket(AR_LISTENING_SOCKET); listenSocket->ar_create(); listenSocket->setSendBufferSize(2048); listenSocket->setReceiveBufferSize(2048); listenSocket->reuseAddress(true); list<string> acceptMask; listenSocket->setAcceptMask(acceptMask); /* Bind to port 8675 */ listenSocket->ar_bind(NULL, 8675); listenSocket->ar_listen(256); /* List of connected arSockets */ socketList = NULL; } bool Ppszg::onStart( arSZGClient& SZGClient ) { /* * Add Transfer Fields to Framework so data is * accessible and syncronized to all clients * Added July 23, 2008 William Davis */ addTransferField("curtime", &(ppszg.clk->curtime), AR_DOUBLE, 1); addTransferField("running", &(ppszg.clk->running), AR_INT, 1); addTransferField("continuous", &(ppszg.clk->continuous), AR_INT, 1); addTransferField("center_", &(ppszg.scene->center_), AR_FLOAT, 3); addTransferField("censize_", &(ppszg.scene->censize_), AR_FLOAT, 1); addInternalTransferField( "commands", AR_CHAR, 1 ); parti_bgcolor( "0 0 0" ); parti_add_commands( pp_parse_args, "partiview", NULL ); parti_add_reader( pp_read, "partiview", NULL ); plugin_init(); // pp_ui_init(); // ppui.view->pickbuffer( COUNT(pickbuffer), pickbuffer ); parti_clip( "0.1", "2500" ); parti_censize( 1.0 ); // parti_pickrange( 3.5 ); parti_object( "g1", NULL, 1 ); readrc( &ppszg.st ); for(int i = 1; i < globalArgc; i++) { specks_read( &ppszg.st, globalArgv[i] ); } // pp_ui_postinit(); return true; } #if 0 bool arMasterSlaveFramework::addInternalTransferField( std::string fieldName, arDataType dataType, int numElements ); The pointer argument is omitted, and the numElements argument now denotes the initial size. The size can be changed by calling: bool arMasterSlaveFramework::setInternalTransferFieldSize( std::string fieldName, arDataType dataType, int newSize ); #endif void Ppszg::onWindowStartGL( arGUIWindowInfo * ) { set_dsp_context( 0 ); /* assign a display context, so texture code can track window changes */ } void Ppszg::onPreExchange( void ) { // called on master only // do navigation... // process commands read from network... // and package any with /* Here we want to do the listening on the sockets to interpret * any commands we might receive over them */ static int socketid = 0; if(ppszg.listenSocket->readable()) { bufferedSocket* sockp = new bufferedSocket(); arSocketAddress addr; ppszg.listenSocket->ar_accept(sockp, &addr); sockp->setID(socketid++); sockp->next = ppszg.socketList; ppszg.socketList = sockp; } cmdbuf_.resize( 1 ); // wipe master->slaves buffer for this cycle /* Now read data into the buffer */ bufferedSocket* sock = ppszg.socketList; bufferedSocket** previousSocketp = &ppszg.socketList; while( sock != NULL ) { int len = sock->poll(); while(len >= 0) { // repeat for all the lines in buffer int maxargs = len/2 + 2; // max possible number of args char **av = new char *[maxargs]; char tbuf[ 2*len + 1024 ]; // temp buffer. Extra room allows for $expansion etc. // XXX should pass this length to tokenize! int ac = tokenize( sock->str(), tbuf, maxargs, av, NULL ); if(ac > 0) { // Not a comment nor a blank line. Try to parse it locally. if( ! specks_parse_args( &ppszg.st, ac, av ) ) { msg("Unrecognized control command: %s", sock->str()); } // In any case, pass it on to slaves PpCmd cmd( CMD_CONTROL, ac, av ); cmd.freezeInto( cmdbuf_ ); } delete av; len = sock->consume(); // Done processing that line. Got another on hand? } if(sock->expired()) { *previousSocketp = sock->next; delete sock; } else { previousSocketp = &sock->next; } sock = *previousSocketp; } // Now ship bundled commands to slaves setInternalTransferFieldSize( "commands", AR_CHAR, cmdbuf_.size() ); if(cmdbuf_.size() > 1 && getenv("CMDTEST")) { PpCmd tcmd; int offset = 0; int room = cmdbuf_.size(); msg("Got %d-bytes of command packet", room-1); // XXX DEBUG while(offset+1 < room) { int oof = offset; tcmd.thawFrom( &cmdbuf_[0], offset, false ); // false -> build argc/argv which point into cmdp string directly msg("Offset %d..%d: command [%d] %s", oof,offset, tcmd.argc, rejoinargs( 0, tcmd.argc, tcmd.argv )); } } int room = 0; char *cmdp = (char *)getTransferField( "commands", AR_CHAR, room ); if(cmdp == 0 || room != cmdbuf_.size()) { msg("panic: transfer field size %d != the size %d we told it to be!\n", room, cmdbuf_.size()); } else { memcpy( cmdp, &cmdbuf_[0], room ); } /* * Added July 23, 2008 William Davis * Forces the framework to listen to updates to navigation matrix * changed from a "jump" command */ navUpdate(); clock_tick( ppszg.clk ); // let time advance } void Ppszg::onPostExchange( void ) // called on master + slaves { if(! this->getMaster() ) { // We're a slave. Decode any commands passed by master this cycle. int room = 0; char *cmdp = (char *)getTransferField( "commands", AR_CHAR, room ); if(cmdp == 0) { static int once = 1; if(once) msg("Ppszg::onPostExchange: can't get transfer field \"commands\""); once = 0; return; } PpCmd pp; int offset = 0; if(room > 1) msg("Got %d-bytes of command packet", room-1); // XXX DEBUG while(offset+1 < room) { pp.thawFrom( cmdp, offset, false ); // false -> build argc/argv which point into cmdp string directly msg("Processing command %s", rejoinargs( 0, pp.argc, pp.argv )); // XXX DEBUG if(pp.type == CMD_CONTROL) { if(! specks_parse_args( &ppszg.st, pp.argc, pp.argv ) ) { msg("Unrecognized control command on slave: %s", rejoinargs( 0, pp.argc, pp.argv )); } } else { msg("can only handle control cmds, not: %s", rejoinargs(0, pp.argc, pp.argv)); } } } } void Ppszg::onWindowInit( void ) { // clear to background... glClearColor( bgcolor.x[0], bgcolor.x[1], bgcolor.x[2], 1 ); ar_defaultWindowInitCallback(); } void Ppszg::onDraw( arGraphicsWindow& win, arViewport& vp ) { /* * Addded July 23, 2008 William Davis * Loads the navigation matrix into the framework, making * sure we listen to the changes enacted by the "jump" command * in parti_setc2w() */ loadNavMatrix(); scene->draw(); } void Ppszg::onDisconnectDraw( void ) { glClearColor( 1, .1, .1, 1 ); glClear( GL_COLOR_BUFFER_BIT ); } void Ppszg::onCleanup( void ) { } void Ppszg::onUserMessage( const string& messageBody ) { // called in master on "dex <N> user <some message for this program>" // parse messageBody, add to command queue for next preExchange time } #ifdef __linux__ #include <GL/glut.h> // hack: Syzygy uses glutWireSphere() in simulator mode, but freeglut insists on glutInit() first #endif int main(int argc, char *argv[]) { #ifdef __linux__ int targc = 1; // rest of hack. glutInit( &targc, argv ); #endif globalArgc = argc; //Make these two variables accessible to the globalArgv = argv; // onStart callback if( ! ppszg.init( argc, argv ) ) { fprintf(stderr, "arMasterSlaveFramework::init() failed, giving up...\n"); exit(1); } if( ! ppszg.start() ) { fprintf(stderr, "arMasterSlaveFramework::start() failed, giving up...\n"); exit(1); } // Shouldn't reach here. fprintf(stderr, "Shouldn't reach here.\n"); exit(2); }