// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include #include "for_soft/shavelib.h" #include "for_soft/softlib.h" // this library depends on opengl32.lib glu32.lib and slii.obj // you must link to them // also, you must have a dongle plugged in and initialized //extern void MAYAexternal_forces(VERT *lastpos, VERT *vect) SOFTGUIDE gd[500]; VERT VNorm( VERT v ) { VERT ret; float len; len = ( float ) sqrt( ( double ) ( v.x * v.x + v.y * v.y + v.z * v.z ) ); ret = v; if( len != 0.0f ) { ret.x /= len; ret.y /= len; ret.z /= len; } if( len == 0.0 ) { ret.x = 1.0; ret.y = 0; ret.x = 0; } return ( ret ); } void main( ) { int killed = 0; int x, slg, hh; // int NUMBER_OF_HAIRS=1; FILE *object; MEMFILE node_data; // this is the holder for the current hair position MEMFILE node_rest; // this is a backup made from the rest position MEMFILE node_state; WFTYPE MAYAdata; // this is the holder for a strand of hair // it gets malloced and written by MAYAmake_a_hair WFTYPE MAYAdata2; // this is the holder for a strand of hair // it gets malloced and written by MAYAmake_a_hair WFTYPE SKINdata; SHAVEPARMS shavep; // printf ("start\n"); object = fopen( "bigfile.obj", "w" ); // open an obj to output to disk // for sanity check only init_geomWF( &MAYAdata ); init_geomWF( &MAYAdata2 ); init_MEMFILE( &node_data, ( long ) 0 ); init_MEMFILE( &node_state, ( long ) 0 ); init_MEMFILE( &node_rest, ( long ) 0 ); //node_data.data=NULL; // this should be set to NULL on startup //node_state.data=NULL; // this should be set to NULL on startup //node_data.size=0; //node_data.pos=0; //MAYAdata.totalverts=0; // these are set to 0 initially so the lib //SKINdata.totalverts=0; // knows not to free them initially. init_geomWF( &SKINdata ); // this is where we initially set up a hair node from an object // the result is 'node_data' gets filled out with an inital groom // and node_state is allocated/reset; // shavep contains the PASSES/CNT info for all 5 groups MAYArefresh( "c:\\models\\extreme\\braid_rest.obj", &node_data, &node_state, &shavep ); for( x = 0; x < 10; x++ ) { int y; SOFTGUIDE sg; SOFTfetch_guide( x, &sg ); for( y = 0; y < 15; y++ ) { sg.select[y] = 0; } SOFTput_guide( x, &sg ); } //SOFTscale_select(0.0f); for( x = 0; x < 20; x++ ) { int y; SOFTGUIDE sg; SOFTfetch_guide( x, &sg ); for( y = 0; y < 1; y++ ) { printf( "select [%d]=%d\n", y, sg.select[y] ); // sg.select[y]=0; } // SOFTput_guideSELECTONLY(x,&sg); } if( node_data.size == 0 ) printf( "check your dongle or file paths in MAYArefresh call\n" ); if( node_data.size > 0 ) { // now lets memcpy this into the the rest version // node_rest.data = ( char * ) malloc( node_data.size * sizeof( char ) ); memcpy( node_rest.data, node_data.data, node_data.size ); node_rest.pos = 0; node_rest.size = node_data.size; // here are the counts let's have a looksee for( x = 0; x < 5; x++ ) printf( "HAIR COUNT[%d]=%d\n", x, shavep.haircount[x] ); { int y; if( 0 == 1 ) SOFTlock_select( ); if( 0 == 1 ) for( y = 0; y < 10; y++ ) { int vv = 0; int done = 1; int xx; SOFTGUIDE guide; while( done > 0 ) { int qq; done = SOFTfetch_guide( vv, &guide ); for( qq = 0; qq < 15; qq++ ) printf( "%f %f %f ", guide.guide[qq].x, guide.guide[qq].y, guide.guide[qq].z ); printf( "\n" ); vv++; } printf( "%d\n", vv ); for( x = 0; x < vv - 1; x++ ) { done = SOFTfetch_guide( x, &guide ); for( xx = 0; xx < 15; xx++ ) guide.guide[xx].y += 1.0; SOFTput_guide( x, &guide ); } } } MAYAwrite_hairDISK( "c:\\shave6\\libtest\\temp.hair" ); // writes the current shave memory to disk as a hair file MAYAread_hairDISK( "c:\\shave6\\libtest\\temp.hair", &node_data, &node_state ); // reads the hair file free( node_rest.data ); node_rest.data = ( char * ) malloc( sizeof( char ) * node_data.size ); node_rest.size = node_data.size; node_rest.pos = 0; // into the memfile struct memcpy( node_rest.data, node_data.data, node_data.size ); // back it up into the rest position MAYArefresh( NULL, &node_rest, &node_state, &shavep ); // technicaly we're just getting shavep here // since the memfiles are already up to date. // after editing for( x = 0; x < 20; x++ ) { int y; SOFTGUIDE sg; SOFTfetch_guide( x, &sg ); for( y = 0; y < 1; y++ ) { printf( "select [%d]=%d\n", y, sg.select[y] ); // sg.select[y]=0; } // SOFTput_guideSELECTONLY(x,&sg); } ////Sleep(10000); ///exit(1); // no let's test xplant //MAYAxplant("c:\\shave6\\libtest\\white.obj"); MAYAxplant( "c:\\ship\\white.obj" ); //MAYArefresh(NULL,&node_rest,&node_state,&shavep); // technicaly we're just getting shavep here // since the memfiles are already up to date. // after editing //printf ("done xplant\n"); free_MEMFILE( &node_data ); free_MEMFILE( &node_state ); MAYAfetch_node( &node_data, &node_state, &shavep ); // now lets step through the groups and make hairs //printf("making a big ass wf obj for sanity check, please wait..\n"); //if (0==1) for( slg = 0; slg < 5; slg++ ) { int blahtfl = 0; int blahtfl2 = 0; // mind you, these haircounts are coming from the shave interface // or its defaults. they can be ANY number, so you can override them // and call as many or as few hairs from each group as you want //if (shavep.haircount[slg]>0) for( hh = 0; hh < shavep.haircount[slg]; hh++ ) { CURVEINFO cinfo; blahtfl2 = 0; // MAYAdata gets resized by this routine, so don't malloc any of the // elements yourself // we're setting pass (the first param) to 0 for the sake of simplicity // normally, shave renders 1-n passes with a different random seed on each // pass for transparency. The SHAVEPARMS variables contain how many passes // we're expecting to make for the current shave group (slg) // MAYAmake_a_hair(0,slg,hh,10,&MAYAdata); // MAKE A HAIR! (10 segs) { if( hh == 1 ) { int trap; trap = 1; } // hh=1; // while (hh==1) MAYAmake_a_curve( 0, slg, hh, 10, &MAYAdata, &cinfo ); // MAKE A HAIR! (10 segs) //printf ("cinfo baserad = %f\n",cinfo.baserad); if( cinfo.killme == 1 ) { printf( "killed hair# %d\n", hh ); killed++; } MAYAmake_a_curveROOT( 0, slg, hh, &MAYAdata2, &cinfo ); // MAKE A HAIR! (10 segs) if( cinfo.killme == 1 ) { printf( "killed hair# %d\n", hh ); killed--; } // printf ("Tpid= %d UTpid %d\n",cinfo.Tpid,cinfo.UTpid); if( MAYAdata.totalverts > 0 ) { float dx, dy, dz; dx = MAYAdata2.v[0].x - MAYAdata.v[0].x; dy = MAYAdata2.v[0].y - MAYAdata.v[0].y; dz = MAYAdata2.v[0].z - MAYAdata.v[0].z; if( ( dx + dy + dz ) > 0 ) printf( "vert %d doesn't match\n", hh ); } } // the hair has been made and now exists as an object in the MAYAdata variable. // mind you, if cloning is on, this structure may contain several hairs // but as far as you're concerned the are just faces anyway // the MAYAdata.color contains vert refferenced color assignments // show the verts (and save them to disk in the object sanity check file) // at this point we're dumping the values to display and to disk // in WFOBJ format for sanity checks. But this is a good refference // for how you want to cycle through the info anyway if( MAYAdata.totalverts > 0 ) for( x = 0; x < MAYAdata.totalverts; x++ ) { fprintf( object, "v %f %f %f\n", MAYAdata.v[x].x, MAYAdata.v[x].y, MAYAdata.v[x].z ); blahtfl2++; } // now dump the face list if( MAYAdata.totalfaces > 0 ) for( x = 0; x < MAYAdata.totalfaces; x++ ) { int y; int g; fprintf( object, "f " ); // start a face for( y = MAYAdata.face_start[x]; y < MAYAdata.face_end[x]; y++ ) { // g=MAYAdata.facelist[y]; g = y; fprintf( object, "%d ", g + 1 ); // +1 for obj file (starts at 0 internally) } fprintf( object, "\n" ); // end of line } blahtfl += blahtfl2; blahtfl2 = 0; } } fclose( object ); // NOW we're going to edit the node this is what you want to do when // the user hits the EDIT button MAYArefresh( NULL, &node_data, &node_state, &shavep ); // load shave memory with this node printf( "killed = %d\n", killed ); SOFTpop_select( .3f ); MAYAwrite_hairDISK( "c:\\shave6\\libtest\\temp.hair" ); // writes the current shave memory to disk as a hair file { FILE *donefile; donefile = fopen( "done", "r" ); if( donefile != NULL ) { fclose( donefile ); system( "del done" ); // this file will flag us that the interface has run // normally the system call will wait, but on // some OS's it will spawn another process // again, watch for path problems here } } printf( "running the interface - you may have to click on this window to get focus again when you exit the interface\n" ); system( "shave.exe c:\\shave6\\libtest\\temp.hair" ); // call the interface (make sure you've got the path right) { // wait around FILE *donefile; donefile = fopen( "done", "r" ); while( donefile == NULL ) { #ifndef _MAC Sleep( 1 ); #endif donefile = fopen( "done", "r" ); } fclose( donefile ); } MAYAread_hairDISK( "c:\\shave6\\libtest\\temp.hair", &node_data, &node_state ); // reads the hair file free( node_rest.data ); node_rest.data = ( char * ) malloc( sizeof( char ) * node_data.size ); node_rest.size = node_data.size; node_rest.pos = 0; // into the memfile struct memcpy( node_rest.data, node_data.data, node_data.size ); // back it up into the rest position MAYArefresh( NULL, &node_rest, &node_state, &shavep ); // technicaly we're just getting shavep here // since the memfiles are already up to date. // after editing // ok now we're going to run some dynamics // normally you fill out the WFobj structure, but in this case // our file originates from disk so I've included an extra routine // to fill it out from a file //MAYAgetobj("c:\\shave6\\libtest\\white.obj",&SKINdata); MAYAgetobj( "c:\\ship\\white.obj", &SKINdata ); // run it // the first time we run xform, we want to flag it -1 // so that it will reset the dynamics (velocities) // we also want to memcpy the node_rest into node_data // every time memcpy( node_data.data, node_rest.data, node_rest.size ); //while (1==1) // test for leaks MAYAxform( &SKINdata, &node_data, &node_state, 0, "statfile0000.stat" ); MAYAxform( &SKINdata, &node_data, &node_state, -1, "statfile0000.stat" ); printf( "doing some dynamics...\n" ); //if (0==1) // no, we're not for( x = 0; x < 10; x++ ) { char statname[255]; printf( "frame %d\n", x ); sprintf( statname, "statfile%04d.stat", x ); // normally you will copy the current positions of the skin into // skin data at each recalc, we'll just leave them in place for this // test memcpy( node_data.data, node_rest.data, node_rest.size ); // flagging it 1 means we're generating dynamics MAYAxform( &SKINdata, &node_data, &node_state, 1, statname ); } // at this point you've got 30 statfiles on disk // calling MAYAxform with the dynamics flag set to 0 // will use the stat file to position the hair printf( "getting frame 5\n" ); memcpy( node_data.data, node_rest.data, node_rest.size ); MAYAxform( &SKINdata, &node_data, &node_state, 0, "statfile0005.stat" ); // at this point you can cycle MAYAmake_a_hair to make some // display hairs if you're shuttling around // but if you're rendering, you might want inbetween frames // for motion blur, here's how: printf( "getting frame 2.3\n" ); memcpy( node_data.data, node_rest.data, node_rest.size ); MAYAset_state( &node_data, &node_state, ( float ) 0.0f, "statfile0003.stat", "statfile0004.stat" ); // SOFTfetch_guide(1,GD); for( x = 1; x < 500; x++ ) { int y; CURVEINFO cinfo; MAYAmake_a_curve( 0, 0, x, 15, &MAYAdata, &cinfo ); for( y = 0; y < 15; y++ ) gd[x].guide[y] = MAYAdata.v[y]; } memcpy( node_data.data, node_rest.data, node_rest.size ); MAYAset_state( &node_data, &node_state, ( float ) .5, "statfile0003.stat", "statfile0004.stat" ); // this yields frame 9.3 // once you've done this you can cycle MAYAmake_a_hair for( x = 1; x < 500; x++ ) { int y; SOFTGUIDE GD; CURVEINFO cinfo; MAYAmake_a_curve( 0, 0, x, 15, &MAYAdata, &cinfo ); for( y = 0; y < 15; y++ ) GD.guide[y] = MAYAdata.v[y]; // SOFTfetch_guide(x,&GD); for( y = 0; y < 15; y++ ) { float dx, dy, dz; dx = GD.guide[y].x - gd[x].guide[y].x; dy = GD.guide[y].y - gd[x].guide[y].y; dz = GD.guide[y].z - gd[x].guide[y].z; if( dx != 0.0f ) printf( "%d - %f %f %f \n ", x, dx, dy, dz ); } } printf( "---------" ); // now let's render printf( "clearing scene (render)\n" ); //MAYArefresh(NULL,&node_data,&node_state,&shavep); MAYAclear_scene( ); { unsigned char *iiimage = NULL; unsigned char *Limage = NULL; float *zbuff = NULL; Matrix vm, camvm; VERT vv, nn, wpos; printf( "adding a hair node to scene (render)\n" ); MAYAadd_hairOPEN( &node_data, &node_state ); MAYAadd_hairCLOSE( &node_state ); // set up a lightsource vv.x = 0.0; // up vector vv.y = 1.0; vv.z = 0.0; wpos.x = ( float ) 1.4; wpos.y = 1.5; wpos.z = 1.5; nn = wpos; nn.x = -nn.x; nn.y = -nn.y; nn.z = -nn.z; nn = VNorm( nn ); printf( "making light matrix (render)\n" ); MAYAmake_view_matrix( vm, vv, nn, wpos, 15.0f ); printf( "adding light to scene (render)\n" ); // allocate an image buffer for the illumination map to be returned in Limage = ( unsigned char * ) malloc( 4 * ( 300 + 1 ) * ( 300 + 1 ) * sizeof( unsigned char ) ); // make the light MAYAadd_light( 1.0f, 1.0f, 1.0f, vm, wpos, 300, 300, 1.0f, 15.0f, 0, 1 ); // set up a camera vv.x = 0.0; // up vector vv.y = 1.0; vv.z = 0.0; nn.x = 0.0; // heading nn.y = 0.0; nn.z = 1.0; wpos.x = 0.0; // world space position wpos.y = ( float ) .2; wpos.z = ( float ) 1.1; nn = wpos; nn.x = -nn.x; nn.y = -nn.y; nn.z = -nn.z; nn = VNorm( nn ); printf( "Making camera view matrix (render)\n" ); MAYAmake_view_matrix( camvm, vv, nn, wpos, 15.0f ); // 30 = fov printf( "adding camera OPEN to scene (render)\n" ); MAYAset_cameraOPEN( camvm, wpos, 320, 200, 1.0f, 15.0f ); printf( "adding camera CLOSE to scene (render)\n" ); MAYAset_cameraCLOSE( camvm, wpos ); printf( "rendering frame\n" ); iiimage = ( unsigned char * ) malloc( 4 * ( 320 + 1 ) * ( 200 + 1 ) * sizeof( unsigned char ) ); MAYArender_frame( &SKINdata, &SKINdata, 1, 1, iiimage, zbuff ); printf( "writing targa file\n" ); //for (x=0;x<320*200*4;x++) printf ("%d ",iiimage[x]); MAYAwrite_targa( "c:\\shave8\\libtest\\test.tga", 320, 200, iiimage ); MAYAwrite_targa( "c:\\shave8\\libtest\\illum.tga", 300, 300, Limage ); free( iiimage ); free( Limage ); } } free_geomWF( &MAYAdata ); free_geomWF( &MAYAdata2 ); free_MEMFILE( &node_data ); free_MEMFILE( &node_state ); free_MEMFILE( &node_rest ); free_geomWF( &SKINdata ); MAYAclear_scene( ); clear_shave_engine( ); }