#define _POSIX_C_SOURCE 200809L //for fileno and strdup #include #include #include #include #include #include #include //code to use select instead of non-blocking is commented out. might decide to use it later. #include #include #include #include "config.h" #include "common.h" #include "math.h" #include "physics.h" #include "input.h" //just the input handler function definitions. #ifdef GRAPHICAL #include "graphics_c3.h" extern struct gra_global gra_global; #endif //TODO: optimizations //TODO: store caches of cs_t, c2_t, and c3_t numbers. //TODO: don't forget to remake gophervr with this. //TODO: XSegment or line structs to store some shit? //TODO: line and triangle intersection for finding what object was clicked on //if I don't do hiliting I only need to calculate that upon click. //TODO: will have to make some pixmaps get resized when the window does. //for now set them to be as big as you think you'll ever resize the window to. struct global global; //might be able to make this faster by just using fgets() and not using recursion and malloc. /* does not return the newline. */ char *read_line_hack(FILE *fp,int len) { short in; char *t; errno=0; switch(in=fgetc(fp)) { case '\n': t=malloc(len+1); t[len]=0; return t; case -1: if(errno == EAGAIN) return 0; if(feof(fp)) { fprintf(stderr,"# reached EOF. exiting.\n"); exit(0); } fprintf(stderr,"# some other error happened while reading. %d %d\n",EAGAIN,errno); perror("hackvr"); exit(1); default: if((t=read_line_hack(fp,len+1))) t[len]=in; break; } return t; } int selfcommand(char *s) { char t; if(!strlen(s)) return 0; ungetc(s[strlen(s)-1],stdin); t=s[strlen(s)-1]; s[strlen(s)-1]=0; selfcommand(s); if(global.periodic_output==0) putchar(t);//output commands immediately return 0; } //warning: clobbers input //skips leading and trailing space. //compresses multiple spaces to one. //returns pointer to array of strings. second argument is return by reference length of returned array. char **line_splitter(char *line,int *rlen) { char **a; int len,i=0; len=1;//we're just counting how much we'll need the first loop through. for(i=0;line[i] && line[i] == ' ';i++);//skip leading space for(;line[i];len++) { for(;line[i] && line[i] != ' ';i++);//skip rest of data for(;line[i] && line[i] == ' ';i++);//skip rest of space } a=malloc(sizeof(char *) * len+1); a[len]=0; len=0;//reuse! for(i=0;line[i] && line[i] == ' ';i++);//skip leading space a[len]=line+i; for(;;) { for(;line[i] && line[i] != ' ';i++);//skip rest of data if(!line[i]) break; line[i++]=0; for(;line[i] && line[i] == ' ';i++);//skip rest of space if(!line[i]) break; a[++len]=line+i; } a[++len]=0; *rlen=len; return a; } //this function is like strcmp //but the first argument MAY use a simple * glob ONLY at the end. //this function may be changed to allow globs in different spots later. int glob_match(char *a,char *b) { if(strchr(a,'*')) { return strncmp(a,b,strchr(a,'*')-a-1);//hack? sure. } return strcmp(a,b); } void hvr_version() { printf("# hackvr version: %s\n",HVR_VERSION); } int load_stdin() {//this function returns -1 to quit, 0 to not ask for a redraw, and 1 to ask for redraw // struct c3_shape s; // struct c3_line l; //move UP, DOWN, LEFT, RIGHT etc shit radians tmprady; //radians tmpradx,tmprady,tmpradz; real tmpx,tmpy,tmpz; char tmp[256]; //^ there c3_group_rot_t *gr; char *command; char *line=0; char *id; char **a=0; int len; int ret=0; int j,k,l; int counter; //struct timeval timeout; //fd_set master; //fd_set readfs; //FD_ZERO(&master); //FD_ZERO(&readfs); //FD_SET(0,&master);//just stdin. int i;//used to store the last triangle. even though I have a global for that. >_> // fprintf(stderr,"# entering load_stdin()\n"); //#ifdef _HACKVR_USE_NONBLOCK_ for(i=0;global.shape[i];i++) ;//hop to the end. fcntl(0,F_SETFL,O_NONBLOCK); if(feof(stdin)) { clearerr(stdin); } //#else /* readfs=master; timeout.tv_sec=0; timeout.tv_usec=1; if((j=select(1,&readfs,0,0,&timeout)) == -1) { perror("select"); return 0; } if(FD_ISSET(0,&readfs)) {*/ //#endif counter=0; // printf("#right before main read loop\n"); // fflush(stdout); for(counter=0;counter < 100 && (line=line?free(line),read_line_hack(stdin,0):read_line_hack(stdin,0));counter++) {//load as long there's something to load if(*line == '#') continue; // fprintf(stderr,"# read command: %s\n",line); if(a) free(a);//use a static char pointer array so I don't have to use the heap. possible optimization. a=line_splitter(line,&len); // for(i=0;i 1) { command=a[1]; } if(len < 2) { if(!strcmp(id,"version")) { hvr_version(); continue; } if(!strcmp(id,"help")) { #ifdef GRAPHICAL fprintf(stderr,"# NOT built headless.\n"); #else fprintf(stderr,"# built headless.\n"); #endif fprintf(stderr,"# commands that don't get prepended with groupname: help, version\n"); fprintf(stderr,"# command format:\n"); fprintf(stderr,"# group names can be globbed in some cases to operate on multiple groups\n"); fprintf(stderr,"# groupnam* command arguments\n"); fprintf(stderr,"# commands:\n"); fprintf(stderr,"# deleteallexcept grou*\n"); fprintf(stderr,"# deletegroup grou*\n"); fprintf(stderr,"# assimilate grou*\n"); fprintf(stderr,"# renamegroup group\n"); fprintf(stderr,"# control grou* [globbing this group could have fun effects]\n"); fprintf(stderr,"# dump\n"); fprintf(stderr,"# quit\n"); fprintf(stderr,"# set\n"); fprintf(stderr,"# addshape color N x1 y1 z1 ... xN yN zN\n"); fprintf(stderr,"# export grou*\n"); fprintf(stderr,"# * scaleup x y z\n"); fprintf(stderr,"# * move x y z\n"); fprintf(stderr,"# * move forward|backward|up|down|left|right\n"); fprintf(stderr,"# that is all.\n"); continue; } else { //fprintf(stderr,"# ur not doing it right. '%s'\n",id); continue; } } ret=1; if(!strcmp(command,"deleteallexcept")) { if(len == 3) { for(j=0;global.shape[j] && j < MAXSHAPES;j++) {//mark first. compress later. if(glob_match(a[2],global.shape[j]->id)) { free(global.shape[j]->id); free(global.shape[j]); global.shape[j]=0; } } for(k=0;kid)) { free(global.group_rot[j]->id); free(global.group_rot[j]); global.group_rot[j]=0; } } for(k=0;kp,rotate_c3_yr(global.shape[j].p[k]); } gr->p.x=gr->p.y=gr->p.z=0; gr->r.x=0; gr->r.y=0; gr->r.z=0; } } //we don't need this gr anymore. //we could clean it up, but its values are already zero. }*/ printf("# this group doesn't have a gr.\n"); } if(!strcmp(command,"deletegroup")) {//should the grouprot get deleted too? sure... if(len == 3) { for(j=0;global.shape[j] && j < MAXSHAPES;j++) { if(!glob_match(a[2],global.shape[j]->id)) { free(global.shape[j]->id); free(global.shape[j]); global.shape[j]=0; } } //we now have an array that needs to be compressed. //max length of j. for(k=0;kid)) { free(global.group_rot[j]->id); free(global.group_rot[j]); global.group_rot[j]=0; } } for(k=0;kid)) { free(global.shape[j]->id); global.shape[j]->id=strdup(id); } } } ret=1; continue; } if(!strcmp(command,"renamegroup")) {//this command doesn't need globbing if(len == 4) { for(j=0;global.shape[j];j++) { if(!strcmp(a[2],global.shape[j]->id)) { free(global.shape[j]->id); global.shape[j]->id=strdup(a[3]); } } gr=get_group_relative(a[2]);//this shouldn't be used here. if(gr) { free(gr->id); gr->id=strdup(a[3]); } } ret=1; continue; } if(!strcmp(command,"status")) { fprintf(stderr,"# loops per second: %d\n",global.lps); continue; } if(!strcmp(command,"dump")) {//same as debug output... and the periodic data. printf("%s set global.camera.p.x %f\n",global.user,global.camera.p.x); printf("%s set global.camera.p.y %f\n",global.user,global.camera.p.y); printf("%s set global.camera.p.z %f\n",global.user,global.camera.p.z); printf("%s set global.camera.r.x %d\n",global.user,global.camera.r.x.d); printf("%s set global.camera.r.y %d\n",global.user,global.camera.r.y.d); printf("%s set global.camera.r.z %d\n",global.user,global.camera.r.z.d); printf("%s set global.zoom %f\n",global.user,global.zoom); continue; } if(!strcmp(command,"quit")) { return -1; } if(!strcmp(command,"set")) { //set variable //TODO: add more things to this. if(len != 3 && len != 4) continue; if(len == 4) { if(0); #ifdef GRAPHICAL else if(!strcmp(a[2],"camera.p.x")) global.camera.p.x=strtold(a[3],0); else if(!strcmp(a[2],"camera.p.y")) global.camera.p.y=strtold(a[3],0); else if(!strcmp(a[2],"camera.p.z")) global.camera.p.z=strtold(a[3],0); else if(!strcmp(a[2],"global.zoom")) global.zoom=strtold(a[3],0); else if(!strcmp(a[2],"camera.r.x")) global.camera.r.x.d=atoi(a[3]); else if(!strcmp(a[2],"camera.r.y")) global.camera.r.y.d=atoi(a[3]); else if(!strcmp(a[2],"camera.r.z")) global.camera.r.z.d=atoi(a[3]); #endif else fprintf(stderr,"# unknown variable: %s\n",a[2]); ret=1; continue; } #ifdef GRAPHICAL if(!strcmp(a[2],"global.beep")) global.beep=1; else if(!strcmp(a[2],"force_redraw")) gra_global.force_redraw^=1; else if(!strcmp(a[2],"red_and_blue")) { gra_global.red_and_blue^=1; set_aspect_ratio(); } #endif else { fprintf(stderr,"# unknown variable: %s\n",a[2]); continue; } fprintf(stderr,"# %s toggled!\n",a[2]); ret=1; continue; } if(!strcmp(command,"control")) {//change what shape key commands affect. if(len > 2) { free(global.user); global.user=strdup(a[2]); // :D } ret=0;//doesn't change anything yet... continue; } if(!strcmp(command,"addshape")) {//need to add a grouprot with this. if(len > 3) { if(len != ((strtold(a[3],0)+(strtold(a[3],0)==1))*3)+4) { fprintf(stderr,"# ERROR: wrong amount of parts for addshape. got: %d expected %d\n",len,((int)strtold(a[3],0)+(strtold(a[3],0)==1))*3+4); fprintf(stderr,"# usage: addshape color number x y z x y z repeated number of time.\n"); continue; } for(i=0;global.shape[i];i++) { if(i>= MAXSHAPES) abort();}//just take me to the end. global.shape[i]=malloc(sizeof(struct c3_shape)); global.shape[i]->len=strtold(a[3],0); global.shape[i]->id=strdup(id); global.shape[i]->attrib.col=strtold(a[2],0); global.shape[i]->attrib.lum=0; for(j=0;j < global.shape[i]->len+(global.shape[i]->len==1);j++) { global.shape[i]->p[j].x=strtold(a[(j*3)+4],0);//second arg is just for a return value. set to 0 if you don't want it. global.shape[i]->p[j].y=strtold(a[(j*3)+5],0); global.shape[i]->p[j].z=strtold(a[(j*3)+6],0); } i++; global.shapes=i; global.shape[i]=0; for(i=0;global.group_rot[i];i++) { if(i >= MAXSHAPES) abort(); if(!strcmp(global.group_rot[i]->id,id)) { break; } } if(global.group_rot[i] == 0) {//we have ourselves a new grouprot! global.group_rot[i]=malloc(sizeof(c3_group_rot_t)); global.group_rot[i]->id=strdup(id); global.group_rot[i+1]=0; global.group_rot[i]->p.x=0; global.group_rot[i]->p.y=0; global.group_rot[i]->p.z=0; global.group_rot[i]->r.x=(degrees){0}; global.group_rot[i]->r.y=(degrees){0}; global.group_rot[i]->r.z=(degrees){0}; } } ret=1; continue; } if(!strcmp(command,"export")) {//dump shapes and group rotation for argument (or all if arg is *) if(len > 2) { for(i=0;global.shape[i];i++) {//require a[2], if not it'll segfault. derrrr, epoch. if(!glob_match(a[2],global.shape[i]->id)) { printf("%s_%s addshape %d %d",id,global.shape[i]->id,global.shape[i]->attrib.col,global.shape[i]->len); for(j=0;j < global.shape[i]->len+(global.shape[i]->len==1);j++) { printf(" %f %f %f",global.shape[i]->p[j].x,global.shape[i]->p[j].y,global.shape[i]->p[j].z); }//possible TODO: should I combine the string and output it all at once instead of throughout a loop? printf("\n"); } } for(i=0;global.group_rot[i];i++) { if(!glob_match(a[2],global.group_rot[i]->id)) { printf("%s_%s rotate %d %d %d\n",id,a[2],global.group_rot[i]->r.x.d,global.group_rot[i]->r.y.d,global.group_rot[i]->r.z.d); printf("%s_%s move %f %f %f\n",id,a[2],global.group_rot[i]->p.x,global.group_rot[i]->p.y,global.group_rot[i]->p.z); } } } continue; } //should scaleup even be inside hackvr? seems like something an external program could do... but it wouldn't act on hackvr's state. so nevermind. if(!strcmp(command,"scaleup")) {//should this scale separately so it can be a deform too? for(i=0;global.shape[i];i++) { if(!glob_match(id,global.shape[i]->id)) { //we're allowing globbing in this command I guess. for(j=0;j < global.shape[i]->len+(global.shape[i]->len==1);j++) { global.shape[i]->p[j].x*=strtold(a[2],0); global.shape[i]->p[j].y*=strtold(len>4?a[3]:a[2],0); global.shape[i]->p[j].z*=strtold(len>4?a[4]:a[2],0); } } } ret=1; continue; } if(!strcmp(command,"rotate")) { if(len > 4) { for(i=0;global.group_rot[i];i++) { if(!strcmp(global.group_rot[i]->id,id)) { break; } } if(global.group_rot[i] == 0) {//we have ourselves a new grouprot! global.group_rot[i]=malloc(sizeof(c3_group_rot_t)); global.group_rot[i]->id=strdup(id); global.group_rot[i+1]=0; global.group_rot[i]->p.x=0;//only set these if new. global.group_rot[i]->p.y=0; global.group_rot[i]->p.z=0; } global.group_rot[i]->r.x=(degrees){(a[2][0]=='+'?global.group_rot[i]->r.x.d:0)+atoi(a[2]+(a[2][0]=='+'))}; global.group_rot[i]->r.y=(degrees){(a[3][0]=='+'?global.group_rot[i]->r.y.d:0)+atoi(a[3]+(a[3][0]=='+'))}; global.group_rot[i]->r.z=(degrees){(a[4][0]=='+'?global.group_rot[i]->r.z.d:0)+atoi(a[4]+(a[4][0]=='+'))}; //now to sanitize them into 0 <= degrees < 360 global.group_rot[i]->r.x.d -= (-(global.group_rot[i]->r.x.d < 0)+(global.group_rot[i]->r.x.d / 360)) * 360; global.group_rot[i]->r.y.d -= (-(global.group_rot[i]->r.y.d < 0)+(global.group_rot[i]->r.y.d / 360)) * 360; global.group_rot[i]->r.z.d -= (-(global.group_rot[i]->r.z.d < 0)+(global.group_rot[i]->r.z.d / 360)) * 360; } ret=1; continue; } if(!strcmp(command,"move")) { if(len > 2) { for(i=0;global.group_rot[i];i++) { if(!strcmp(global.group_rot[i]->id,id)) { break; } } if(global.group_rot[i] == 0) {//we have ourselves a new grouprot! global.group_rot[i]=malloc(sizeof(c3_group_rot_t)); global.group_rot[i]->id=strdup(id); global.group_rot[i+1]=0; global.group_rot[i]->r.x=(degrees){0};//only set these if new. global.group_rot[i]->r.y=(degrees){0}; global.group_rot[i]->r.z=(degrees){0}; } } if(len > 4) { //if we have > 4 we're doing relative movement global.group_rot[i]->p.x=(a[2][0]=='+'?global.group_rot[i]->p.x:0)+strtold(a[2]+(a[2][0]=='+'),0); global.group_rot[i]->p.y=(a[3][0]=='+'?global.group_rot[i]->p.y:0)+strtold(a[3]+(a[3][0]=='+'),0); global.group_rot[i]->p.z=(a[4][0]=='+'?global.group_rot[i]->p.z:0)+strtold(a[4]+(a[4][0]=='+'),0); } else if(len > 2) { tmpy=0; if(!strcmp(a[2],"forward")) { tmprady=d2r((degrees){global.camera.r.y.d}); //snprintf(tmp,sizeof(tmp)-1,"%s move +%f +0 +%f\n",global.user,tmpx,tmpz); } else if(!strcmp(a[2],"backward")) { tmprady=d2r((degrees){global.camera.r.y.d+180}); //snprintf(tmp,sizeof(tmp)-1,"%s move +%f +0 +%f\n",global.user,tmpx,tmpz); } else if(!strcmp(a[2],"up")) { tmprady=d2r((degrees){global.camera.r.y.d});//doesn't matter. tmpy=WALK_SPEED*1; } else if(!strcmp(a[2],"down")) { tmprady=d2r((degrees){global.camera.r.y.d});//doesn't matter. yet. tmpy=WALK_SPEED*1; } else if(!strcmp(a[2],"left")) { tmprady=d2r((degrees){global.camera.r.y.d+90}); //snprintf(tmp,sizeof(tmp)-1,"%s move +%f +0 +%f\n",global.user,tmpx,tmpz); } else if(!strcmp(a[2],"right")) { tmprady=d2r((degrees){global.camera.r.y.d+270}); //snprintf(tmp,sizeof(tmp)-1,"%s move +%f +0 +%f\n",global.user,tmpx,tmpz); } else { fprintf(stderr,"# dunno what direction you're talking about. try up, down, left, right, forward, or backward\n"); continue; } tmpx=WALK_SPEED*sin(tmprady.r);//the camera's y rotation. tmpz=WALK_SPEED*cos(tmprady.r);//these are only based on snprintf(tmp,sizeof(tmp)-1,"%s move +%f +%f +%f\n",global.user,tmpx,tmpy,tmpz); selfcommand(tmp); } else { fprintf(stderr,"# ERROR: wrong amount of parts for move. got: %d expected: 4 or 2\n",len); } ret=1; continue; } fprintf(stderr,"# I don't know what command you're talking about. %s\n",command); //I used to have free(line) here, but this place is never gotten to if a command is found so it wasn't getting released. } return ret; } int export_file(FILE *fp) {//not used yet. maybe export in obj optionally? no. that should be an external program // struct c3_shape *to; // int i; // for(i=0;global.shape[i];i++) { // to=global.shape[i]; // printf("%s addshape %f %f %f %f %f %f %f %f %f\n",to->id,to->p1.x,to->p1.y,to->p1.z,to->p2.x,to->p2.y,to->p2.z,to->p3.x,to->p3.y,to->p3.z); // } return 0; } int main(int argc,char *argv[]) { char redraw; time_t oldtime; unsigned int lps; c3_t old_p; c3_rot_t old_r; if(argc == 2) { if(!strcmp(argv[1],"-v") || !strcmp(argv[1],"--version")) { hvr_version(); return 0; } if(!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) { printf("usage: hackvr file1 file2 file3 ... fileN < from_others > to_others\n"); return 0; } } if(argc == 1) { //we're just doing stdin } if(argc > 1) { //open every argument and add it to list of files to read.. //I could just read } global.user=getenv("USER"); fcntl(1,F_SETFL,O_NONBLOCK);//won't work setbuf(stdin,0); setbuf(stdout,0); global.debug=DEBUG; global.periodic_output=PERIODIC_OUTPUT; global.beep=0; #ifdef GRAPHICAL graphics_init(); #endif fprintf(stderr,"# entering main loop\n"); for(lps=0;;lps++) { switch(redraw=load_stdin()) {//this needs to loop over all opened files. case -1: return 0; break; case 0: break; default: break; } if(time(0) != oldtime) { global.lps=lps; lps=0; oldtime=time(0); } //fprintf(stderr,"# applying physics...\n"); redraw |= apply_physics(); //fprintf(stderr,"# derping.\n"); if(global.periodic_output == 1) {//this is the same type of thing the debug output does. debug output now goes here. #ifdef GRAPHICAL printf("# loops per second: %d mouse.x: %f mouse.y: %f\n",global.lps,gra_global.mouse.x,gra_global.mouse.y); #else printf("# loops per second: %d\n",global.lps); #endif global.periodic_output = PERIODIC_OUTPUT; //output any difference between current camera's state //and the camera state the last time we output it. if(memcmp(&old_p,&global.camera.p,sizeof(c3_t))) { old_p.x=global.camera.p.x; old_p.z=global.camera.p.z; old_p.y=global.camera.p.y; printf("%s move %f %f %f\n",global.user,old_p.x,old_p.y,old_p.z); } if(memcmp(&old_r,&global.camera.r,sizeof(c3_rot_t))) { old_r.x=global.camera.r.x; old_r.y=global.camera.r.y; old_r.z=global.camera.r.z; printf("%s rotate %d %d %d\n",global.user,global.camera.r.x.d,global.camera.r.y.d,global.camera.r.z.d); } } global.periodic_output--; #ifdef GRAPHICAL if(graphics_event_handler(redraw) == -1) break;//this thing should call draw_screen when it needs to. if(mouse_event_handler() == -1) break; if(keyboard_event_handler() == -1) break; #endif sleep(.01); } return 0; }