Newer
Older
/*
* Syzygy-related functions for szgPartiview, including main program.
*/
static char local_id[] = "$Id$";
/*
* $Log$
* Revision 1.17 2009/03/28 22:48:47 slevy
* Add some debug code (matstr()).
* Re-orthogonalize the matrix in FlyNav::navUpdate().
* Otherwise it'll blow up within a few dozen iterations.
*
* Revision 1.16 2009/03/27 16:30:29 slevy
* Add first attempt at flyer navigation.
* New "-fly" config option, and "fly <buttonpov>,<buttoncenter>,<tspeed>,<rspeed>"
* control command. I don't know which button is which, so it's configurable on the fly.
*
* Revision 1.15 2009/03/27 03:26:51 slevy
* When "jump"ing, align the target point with the head's position,
* rather than the CAVE origin. Use position only -- keep the orientation
* of the CAVE.
*
* This still might not be right. Might be better to put the
* target point just behind the head, so an origin crosshair doesn't bug us.
*
slevy
committed
* Revision 1.14 2009/03/26 20:47:13 slevy
* Add -plog option to control logging. If present, each szg process logs all msg()/warn() messages
* to "parti.NNNN.log" for ProcessID NNNN.
*
* Revision 1.13 2009/03/25 07:47:39 slevy
* ar_log_XXX logs don't seem to go anywhere, so also append to
* parti.NNNN.log, where NNNN is the SZGClient ProcessID.
* Maybe this too should be changed -- should we just try to log
* to one big file, to limit clutter? Every line is already labelled with [PID] now.
*
* Don't use exit(0) in command parser. Set a shared quit flag instead,
* and have everybody (?) exit later.
*
* <string>.append( C_string, len ) never copies in the string's trailing \000,
* so add it ourselves.
*
* Only create a listenSocket if we're the master -- avoid races to grab the port.
* I'm not sure when is best to determine master-hood, so out of superstition
* we do it in the first preExchange callback.
*
* Yes, we *do* need to initialize cmdbuf_ to be *empty*.
*
* 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"
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#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;
// 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];
}
/* =================================================================== */
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
class FlyNav {
public:
bool flying; // Flying enabled? If not, ignore all the below.
int bpov; // While this button# pressed, fly, with point-of-view as rotation center
int bcen; // While this button# pressed, fly, with "center" as rot. center
float tspeed; // translation speed scale factor
float rspeed; // rotation speed scale factor
int bstarted; // has flying started? If so, on which button? Else -1.
Matrix TstartW2C; // wand-to-cave tfm at time of whichever button press
Matrix TstartC2W; // cave-to-wand, ditto
FlyNav();
void setPovButton( int b ) { bpov = b; }
void setCenterButton( int b ) { bcen = b; }
void navUpdate();
void config( const char * s );
};
FlyNav::FlyNav() {
flying = false;
bpov = 0;
bcen = 1;
tspeed = 2;
rspeed = 2;
bstarted = -1;
TstartW2C = Tidentity; // initial Wand-to-Cave matrix
TstartC2W = Tidentity; // initial Cave-to-Wand matrix (inverse of Wand-to-Cave)
}
void FlyNav::config( const char *str ) {
flying = true;
if(0 == strcmp(str, "off")) {
flying = false;
return;
} else if(0 == strcmp(str, "on")) {
flying = true;
return;
}
sscanf( str, "%d%*c%d%*c%f%*c%f%*c", &bpov, &bcen, &tspeed, &rspeed );
}
char *matstr( Matrix *T )
{
static char s[128];
sprintf(s, "%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g",
T->m[0],T->m[1],T->m[2],T->m[3],
T->m[4],T->m[5],T->m[6],T->m[7],
T->m[8],T->m[9],T->m[10],T->m[11],
T->m[12],T->m[13],T->m[14],T->m[15]);
return s;
}
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
void FlyNav::navUpdate() {
if(!flying)
return;
arMatrix4 wand2C = ppszg.getMatrix( 1 ); // wand-to-CAVE tfm
if(this->bstarted < 0) {
if(this->bpov >= 0 && ppszg.getOnButton( bpov )) {
this->bstarted = this->bpov;
} else if(this->bcen >= 0 && ppszg.getOnButton( this->bcen )) {
this->bstarted = this->bcen;
}
if(this->bstarted >= 0) {
copyfrom_arMatrix( &this->TstartW2C, &wand2C );
eucinv( &this->TstartC2W, &this->TstartW2C );
}
} else {
if(ppszg.getButton( this->bstarted )) {
// How much has wand moved since button was pressed?
arMatrix4 arC2world = ar_getNavMatrix();
Matrix TC2world;
copyfrom_arMatrix( &TC2world, &arC2world );
// TC2world is CAVE-to-world transform
float deltat = 0.001 * ppszg.getLastFrameTime();
Matrix TnowW2C; // wand-to-CAVE
copyfrom_arMatrix( &TnowW2C, &wand2C );
Matrix TdeltaC;
vgettranslation( &pnowC, &TnowW2C );
vsub( &dpC, &pnowC, &pstartC );
float mag = vlength( &dpC );
vscale( &dpC, sqrt(mag) * this->tspeed * deltat, &dpC );
// now dpC is the incremental translation in CAVE-oriented coords
Quat dqC;
tfm2quat( &dqC, &TdeltaC );
Quat qidentity = { 1, 0, 0, 0 };
if(dqC.q[0] < 0)
qidentity.q[0] = -1; /* interpolate in same quat hemisphere */
float qfrac = this->rspeed * deltat;
qcomb( &dqC, qfrac,&dqC, 1-qfrac,&qidentity );
qnorm( &dqC, &dqC );
Matrix TincrC;
quat2tfm( &TincrC, &dqC );
// now TincrC is the incremental rotation in CAVE-oriented coordinates
// stuff the translation in too
vsettranslation( &TincrC, &dpC );
// where is the center-of-rotation? in either CAVE or world coordinates
const Point *pcenC, *pcenW;
if(this->bstarted == this->bpov) {
pcenC = &pnowC; // rotate about the wand
pcenW = NULL;
} else {
pcenC = NULL;
pcenW = ppszg.scene->center();
}
Matrix TnewC2world;
mconjugate( &TnewC2world, &TC2world, &TincrC,
&TC2world, NULL/*world2C auto-computed*/,
pcenW, pcenC );
/* re-orthogonalize */
float scaling = vlength( (Point *)&TC2world.m[0] );
Point p;
Quat q;
vgettranslation( &p, &TnewC2world );
tfm2quat( &q, &TnewC2world );
quat2tfm( &TnewC2world, &q );
for(int i = 0; i < 12; i++)
TnewC2world.m[i] *= scaling;
vsettranslation( &TnewC2world, &p );
arMatrix4 arnewC2world;
copyto_arMatrix( &arnewC2world, &TnewC2world );
ar_setNavMatrix( arnewC2world );
} else {
bstarted = -1;
}
}
}
// Global instance
FlyNav flyer;
////////////////////////////////////////////////////////////////////////
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("%s: subcam: expected name az el rol L R B T, not %s",
fromfname, rejoinargs( 1, argc, argv ));
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
}
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}};
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
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;
static MsgLevel msglevel = REMARK;
static int processid = 42;
slevy
committed
static int partiLog = 0;
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);
ar_log_remark() << str;
ar_log_warning() << str;
slevy
committed
if(partiLog) {
char logfname[128];
sprintf(logfname, partiLog > 1 ? "parti.%04d.log" : "parti.log", processid);
FILE *f = fopen(logfname, "a");
if(f != 0) {
fprintf(f, "[%d]: %s\n", processid, str);
fclose(f);
}
}
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);
}
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 *Tc2w ) {
//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 h2C = ppszg.getMatrix( 0 ); // get head-to-Cave transform
// Ignore head orientation, just use its position,
// and take shortcut rather than eucinv for inverse.
Matrix invTh2C = Tidentity;
invTh2C.m[12] = -h2C.v[12];
invTh2C.m[13] = -h2C.v[13];
invTh2C.m[14] = -h2C.v[14];
#else
invTh2C.m[12] = 0;
invTh2C.m[13] = -5;
invTh2C.m[14] = -3.5;
#endif
Matrix Thead2world;
mmmul( &Thead2world, &invTh2C, Tc2w );
arMatrix4 jumpto;
copyto_arMatrix( &jumpto, &Thead2world );
arMatrix4 wasat = ar_getNavMatrix();
ar_setNavMatrix( jumpto );
arMatrix4 nowat = ar_getNavMatrix();
arMatrix4 hdmat = ppszg.getMatrix( 0 );
msg("head: %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f",
hdmat.v[0],hdmat.v[1],hdmat.v[2],hdmat.v[3],
hdmat.v[4],hdmat.v[5],hdmat.v[6],hdmat.v[7],
hdmat.v[8],hdmat.v[9],hdmat.v[10],hdmat.v[11],
hdmat.v[12],hdmat.v[13],hdmat.v[14],hdmat.v[15]);
msg("navwas: %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f",
wasat.v[0],wasat.v[1],wasat.v[2],wasat.v[3],
wasat.v[4],wasat.v[5],wasat.v[6],wasat.v[7],
wasat.v[8],wasat.v[9],wasat.v[10],wasat.v[11],
wasat.v[12],wasat.v[13],wasat.v[14],wasat.v[15]);
msg("setc2w: %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f",
Tc2w->m[0],Tc2w->m[1],Tc2w->m[2],Tc2w->m[3],
Tc2w->m[4],Tc2w->m[5],Tc2w->m[6],Tc2w->m[7],
Tc2w->m[8],Tc2w->m[9],Tc2w->m[10],Tc2w->m[11],
Tc2w->m[12],Tc2w->m[13],Tc2w->m[14],Tc2w->m[15]);
msg("navnow: %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f",
nowat.v[0],nowat.v[1],nowat.v[2],nowat.v[3],
nowat.v[4],nowat.v[5],nowat.v[6],nowat.v[7],
nowat.v[8],nowat.v[9],nowat.v[10],nowat.v[11],
nowat.v[12],nowat.v[13],nowat.v[14],nowat.v[15]);
}
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
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
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;
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
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 );
}
if(!needidle && ppszg.playidling) {
// XXX disable calling playidle() here // Fl::remove_idle( playidle, NULL );
}
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);
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
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
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
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" )) {
msg("exiting");
// ppszg.stop( 1 );
ppszg.quitnow_ = 1;
} else if(!strcmp( argv[0], "fly" )) {
if(argc > 1)
flyer.config( argv[1] );
msg("fly %s povbutton %d centerbutton %d tspeed %g rspeed %g (\"fly %d,%d,%g,%g\"",
flyer.flying ? "on" : "off",
flyer.bpov, flyer.bcen, flyer.tspeed, flyer.rspeed,
flyer.bpov, flyer.bcen, flyer.tspeed, flyer.rspeed);
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
} 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",