// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 static void MTdraw_lotsWF_common( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, GLOBS * gs,int segs, int type); static void Smk_polymatRS( Matrix m, int n, GLOBS *gs ); void make_basematrix(BASEHAIR *h,BASEMATRIX *bm); void twist_basehair (BASEHAIR *clumpCenter, BASEHAIR *h,GLOBS *gs); static void MTcolor_a_hairRS( BASEHAIR * h, GLOBS * gs ); static void mk_polymatRS2( Matrix m, int n,VERT h,GLOBS *gs ); void MTcolor_clumpy( BASEHAIR *,BASEHAIR *, GLOBS * ); void generate_clump_centerRS(BASEHAIR *ret,CURVEINFO *cinfo2,VERT p,int slg,GLOBS *gs); void generate_clump_center(BASEHAIR *ret,CURVEINFO *cinfo2,VERT p,int slg,GLOBS *gs); void MTdraw_lotsWFRSROOT( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, int samp, GLOBS * gs ); void MTdraw_lotsWFRSROOTREST( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo,int samp, GLOBS * gs ); void MTdraw_lotsWFROOTREST( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, int samp,GLOBS * gs ); static void MTdisplace_center( BASEHAIR * h, float amp,GLOBS * gs ); void MTdraw_lotsWF( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, GLOBS * gs ) { MTdraw_lotsWF_common( slg, ind, offs, clone, outhair, cinfo, gs,15,0 ); } void MTdraw_lotsWFRS( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, GLOBS * gs ) { MTdraw_lotsWF_common( slg, ind, offs, clone, outhair, cinfo, gs,15,1 ); } void MTdraw_lotsWFROOT( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, GLOBS * gs ) { // gs->Gcurpass=samp; MTdraw_lotsWF_common( slg, ind, offs, clone, outhair, cinfo, gs,0,0 ); } void MTdraw_lotsWFRSROOT( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, int samp,GLOBS * gs ) { // gs->Gcurpass=samp; clone=0; MTdraw_lotsWF_common( slg, ind, offs, clone, outhair, cinfo, gs,0,1 ); } static void MTdraw_lotsWF_common( int slg, int ind, VERT offs, int clone, BASEHAIR * outhair, CURVEINFO * cinfo, GLOBS * gs,int segs, int type) { int indx; BASEHAIR clumpCenter; int nokink = 1; int pd; float jfloat; float ascalh, ascalr; VERT t1[15], t2[15]; int qv; VERT xv, yv, zv, b; int pid = 0; int killit = 0; BASEHAIR h; float d1 = 1.0, d2 = 1.0f; BASEHAIR hrest; int x; int cc; int cc2; int cloneON = 0; SHAVEPARMS *lGshavep; int chk = 1; int lp; BASEMATRIX bm; BASEHAIR MTLOTSWFlastone; BASEHAIR MTLOTSWFlasthrest; int MTLOTSWFlastpid; CURVEINFO MTLOTSWFlastcinfo; float lGtex_info[60]; // these need to be set int lCurpass; // what is the difference between cursamp and curpass? int lLCL_CNT; int lLCL_PASSES; float lSlidervals[60]; // load these up unsigned long lShaveID; int lTotal_lSlgfaces; int lDontinterp; int lShadcnt; int lClumps; VERT lGdisplace; int lNodeID; float lRestBOUNDLENGTH; int lTotal_slgfaces; int lTotal_splines; int **lSlgfaces; // slgfaces[slg] int *lDlink; int lGnodraw; int lgenON; VERT spec_tint; VERT spec_tint2; gs->Gcurpass=0; gs->passnumber=0; Gpass=0; cursamp=0; if (type==0) { gs->rs.Gsquirrel=Gsquirrel; lgenON=genON; lGshavep=Gshavep; lGnodraw=Gnodraw; lDlink=Dlink; lCurpass=Gcurpass; lSlgfaces=slgfaces; gs->rs.Gflyaway_percent=Gflyaway_percent; // lNodeid=SHAVEID; lNodeID=GnodeID; lShaveID=SHAVEID; lLCL_CNT=LOCAL_CNT[slg]; lLCL_PASSES=LOCAL_PASSES[slg]; for (qv=0;qv<60;qv++) lSlidervals[qv]=sliders[qv][slg].value; lNodeID=GnodeID; lShaveID=SHAVEID; lTotal_lSlgfaces=total_slgfaces[slg]; lDontinterp=Gdontinterp; lClumps=Gclumps; lGdisplace= Gdisplace; lRestBOUNDLENGTH=restBOUNDLENGTH; lTotal_slgfaces=total_slgfaces[slg]; lTotal_splines=total_splines; lCurpass=Gcurpass; lShadcnt=LOCAL_SHADCNT[slg]; GhairID=ind; gs->random_seed_offset=Grandom_seed_offset; spec_tint=Gspec_tint; spec_tint2=Gspec_tint2; } else { lgenON=gs->rs.genON; lGshavep=Gshavep; lGnodraw=Gnodraw; spec_tint=gs->rs.Gspec_tint; spec_tint2=gs->rs.Gspec_tint2; lDlink=gs->rs.Dlink; lCurpass=gs->Gcurpass; lSlgfaces=gs->rs.slgfaces; lNodeID=gs->rs.nodeID ; lLCL_CNT=gs->rs.LOCAL_CNT[slg]; lLCL_PASSES=gs->rs.LOCAL_PASSES[slg]; for (qv=0;qv<60;qv++) lSlidervals[qv]=gs->rs.sliders[qv][slg].value; lShaveID=(unsigned long) gs->rs.SHAVEID; lTotal_lSlgfaces=gs->rs.total_slgfaces[slg]; lDontinterp=gs->rs.Gdontinterp; lClumps=gs->rs.Gclumps; lRestBOUNDLENGTH=gs->rs.restBOUNDLENGTH; lTotal_slgfaces=gs->rs.total_slgfaces[slg]; lTotal_splines=gs->rs.total_splines; lShadcnt=gs->rs.LOCAL_SHADCNT[slg]; GhairID=ind; gs->GhairID=ind; lGdisplace= gs->rs.Gdisplace; gs->random_seed_offset=gs->rs.random_seed_offset; } lCurpass=0; Gslg=slg; GhairID=ind; //GnodeID=lNodeID; #ifdef docache memcpy( &MTLOTSWFlastone, &gs->MTLOTSWFlastone, sizeof( BASEHAIR ) ); memcpy( &MTLOTSWFlastcinfo, &gs->MTLOTSWFlastcinfo, sizeof( CURVEINFO ) ); memcpy( &MTLOTSWFlasthrest, &gs->MTLOTSWFlasthrest, sizeof( BASEHAIR ) ); MTLOTSWFlastpid = gs->MTLOTSWFlastpid; #endif // MTLOTSWFlastone = &gs->MTLOTSWFlastone; // MTLOTSWFlasthrest = &gs->MTLOTSWFlasthrest; // MTLOTSWFlastpid = &gs->MTLOTSWFlastpid; // MTLOTSWFlastcinfo = &gs->MTLOTSWFlastcinfo; if (segs==0) { lClumps=0; // don't do any clumping on segs clone=0; // don't do mult-splay } { int cll; cll=lSlidervals[25]; if (cll==0) cll=1; indx=ind; if (clone>0) indx= ind*cll+clone;//+ cll*lCurpass*lLCL_CNT*lLCL_PASSES; } cinfo->shadowHair = 1; init_a_hair( &hrest ); init_a_hair( &h ); if (segs>0) // hrm if( lSlidervals[25] > 0 ) cloneON = 1; { lp = lLCL_PASSES; //lp-=1;if (lp<1) lp=1; cc2 = ( lCurpass ) % lp; cc = cc2; cc2=0; } lCurpass = cc2; cinfo->depthpass = cc2; h.killme = 0; cinfo->killme = 0; ascalr = 1.0f; ascalh = ascalr; h.killme = 0; hrest.killme = 0; cinfo->killme = 0; killit = 0; hrest.killme = 0; cinfo->killme = 0; cinfo->diff = 0.0f; cinfo->kspec = 0.0f; cinfo->norm.x = 0.0f; cinfo->norm.y = 1.0f; cinfo->norm.z = 0.0f; cinfo->shad = 0.0f; cinfo->spec = 0.0f; cinfo->tiprad = 0.0f; cinfo->baserad = 0.0f; cinfo->hairID = ind; cinfo->nodeID = lNodeID; cinfo->depthpass = cc2; cinfo->ambient = 0.0f; cinfo->restlength = 0.0f; cinfo->cutlength = 0.0f; cinfo->mtl = slg; cinfo->u = 0; cinfo->v = 0; cinfo->u2 = 0; cinfo->v2 = 0; x = ind; if( lgenON == 0 ) { killit = 1; h.killme = 1; cinfo->killme = 1; chk = 0; hrest.killme = 1; } if( DEGENERATE_OBJECT == 1 ) { killit = 1; h.killme = 1; cinfo->killme = 1; chk = 0; hrest.killme = 1; } #ifdef docache if( ( cloneON ) && ( clone > 0 ) ) { int tt; memcpy( &h, &MTLOTSWFlastone, sizeof( BASEHAIR ) ); memcpy( cinfo, &MTLOTSWFlastcinfo, sizeof( CURVEINFO ) ); memcpy( &hrest, &MTLOTSWFlasthrest, sizeof( BASEHAIR ) ); memcpy( &t2, gs->MTT2, sizeof( VERT ) * 15 ); pid = gs->MTLOTSWFlastpid; } #endif if( lDontinterp == 1 ) if( slg != 4 ) { if( ind > lTotal_lSlgfaces - 1 ) { chk = 0; killit = 1; hrest.killme = 1; cinfo->killme = 1; } if( ind < lTotal_lSlgfaces ) { chk = 1; killit = 0; hrest.killme = 0; cinfo->killme = 0; pid = lSlgfaces[slg][ind]; } } if( lDontinterp == 1 ) if( slg == 4 ) { if( ind > lTotal_lSlgfaces - 1 ) { chk = 0; killit = 1; hrest.killme = 1; cinfo->killme = 1; } if( ind < lTotal_lSlgfaces ) { pid = lSlgfaces[slg][ind]; chk = 1; killit = 0; hrest.killme = 0; cinfo->killme = 0; } } { MTJsrand(ind,gs); mixitup( ); { float rand1; float rand2; rand1 = ( float ) MTdrand98( gs ); rand2 = ( float ) MTdrand98( gs ); for( pd = 0; pd < ( int ) ( 37. * rand1 + 10. * cc * rand2 ); pd++ ) { float jjunk; jjunk = ( float ) MTdrand98( gs ); } } } if( ( ( lLCL_CNT == 0 ) && ( lShadcnt == 0 ) ) || ( lTotal_slgfaces == 0 ) ) { killit = 1; h.killme = 1; cinfo->killme = 1; } if( lGnodraw == 1 ) { killit = 1; h.killme = 1; cinfo->killme = 1; } if( cachemode == PLAYBACK_CACHE ) { killit = 0; chk = 1; h.killme = 0; cinfo->killme = 0; } #ifdef docache if( ( cloneON ) * ( clone ) == 0 ) #endif { if( cachemode != PLAYBACK_CACHE ) if( lDontinterp == 0 ) if( killit == 0 ) if( lTotal_lSlgfaces > 0 ) if( lLCL_CNT > 0 ) { if( slg == 4 ) pid = (int)( floor ) ( ( float ) MTdrand98( gs ) * ( ( double ) lTotal_lSlgfaces - 1 ) - .000001f ); if( slg != 4 ) { int rp = 0; rp = ( int ) ( ( float ) MTdrand98( gs ) * ( ( double ) lTotal_lSlgfaces ) - .000001f ); if( rp > lTotal_lSlgfaces - 1 ) rp = lTotal_lSlgfaces - 1; pid = lSlgfaces[slg][rp]; } if( lTotal_lSlgfaces > 0 ) if( pid > lTotal_lSlgfaces - 1 ) pid = lTotal_lSlgfaces - 1; chk = 1; if( killit == 0 ) if( lTotal_lSlgfaces > 0 ) if( lLCL_CNT > 0 ) if( slg != 4 ) { if (type==0) while( ( MTcheckface( pid, gs ) == 0 ) ) //||(pid==lTotal_lSlgfaces)) { int rp; rp = ( int ) ( ( float ) MTdrand98( gs ) * ( ( double ) lTotal_lSlgfaces ) - .000001f ); if( rp > lTotal_lSlgfaces - 1 ) rp = lTotal_lSlgfaces - 1; pid = lSlgfaces[slg][rp]; } if (type==1) while( ( MTcheckfaceRS( pid, gs ) == 0 ) ) //||(pid==lTotal_lSlgfaces)) { int rp; rp = ( int ) ( ( float ) MTdrand98( gs ) * ( ( double ) lTotal_lSlgfaces ) - .000001f ); if( rp > lTotal_lSlgfaces - 1 ) rp = lTotal_lSlgfaces - 1; pid = lSlgfaces[slg][rp]; } } } } if (0==1) if( base_cache != NULL ) if( cachemode == PLAYBACK_CACHE ) pid = base_cache[current_cachehair].pid; // if (type==0) // if( cloneON * ( clone ) == 0 ) // MTLOTSWFlastpid = pid; // if (type==1) // if( cloneON * ( clone ) == 0 ) // gs->MTLOTSWFlastpid = pid; if (type==0) if( ( cloneON ) && ( clone > 0 ) ) { // pid = gs->MTLOTSWFlastpid; } if (type==1) if( ( cloneON ) && ( clone > 0 ) ) { // pid = gs->MTLOTSWFlastpid; } if( lDontinterp == 1 ) { if( ( slg == 4 ) && ( ind > lTotal_splines - 1 ) ) chk = 0; if( chk == 0 ) { h.killme = 1; hrest.killme = 1; killit = 1; cinfo->killme = 1; } } if( chk == 0 ) { h.killme = 1; hrest.killme = 1; killit = 1; cinfo->killme = 1; } if (segs==0) { MTJsrand( ( unsigned long ) ind, gs ); { if (type==0) hrest = MTgen_resthair( pid, slg, cinfo, gs ); if (type==1) hrest = MTgen_resthairRS( pid, slg, cinfo, gs ); } { int qq; for( qq = 0; qq < 15; qq++ ) hrest.hv[qq] = hrest.noisev[qq]; } // new if( hrest.restlength < 0.00001f ) { hrest.killme = 1; killit = 1; cinfo->killme = 1; } { int qq; for (qq=0;qq<60;qq++) hrest.slider[qq] = 1.0f; } if( TEXCACHEMODE == 1 ) { int qq; int tt = 0; float *Gtc; int gttc; lGdisplace.x=0; lGdisplace.y=0; lGdisplace.z=0; Gtc=Gtex_cacheinfo[lNodeID][slg]; gttc=Gtex_totalchannels[lNodeID][slg]; if (Gtex_cachedisplace[lNodeID][slg]) lGdisplace = Gtex_cachedisplace[lNodeID][slg][ind]; // + lCurpass * lGshavep[lNodeID].haircount[slg]]; tt=0; for( qq = 0; qq < 60; qq++ ) { lGtex_info[qq] = 1.0f; if( Gtex_channellink[lNodeID][qq] >= 0 ) { lGtex_info[qq] = Gtc[tt+gttc*ind];// + lCurpass * gttc + gttc * ind * lGshavep[lNodeID].passes[slg]]; tt++; } } } { int pp; if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 1 ) for( pp = 0; pp < 60; pp++ ) { int tt; hrest.slider[pp] = lGtex_info[pp]; } } hrest.hairnumber=ind; cinfo->depthpass=lCurpass; Gslg=slg; GhairID=ind; //GnodeID=lNodeID; MTJsrand( ( unsigned long ) indx, gs ); hrest.hairnumber=ind; if (type==0) MTcolor_a_hair( &hrest,gs ); else MTcolor_a_hairRS( &hrest,gs ); MTJsrand( ( unsigned long ) indx, gs ); if( cachemode != PLAYBACK_CACHE ) { int pp; cinfo->mtl = slg; cinfo->groupID = slg; cinfo->hairID=ind; cinfo->depthpass=0; cinfo->mtl = slg; cinfo->groupID = slg; Gslg=slg; GhairID=ind; //GnodeID=lNodeID; if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 0 ) for( pp = 0; pp < 60; pp++ ) hrest.slider[pp] = SHAVEapply_texture( cinfo, hrest.hv[0], lShaveID, pp, hrest.slider[pp] ); if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 1 ) for( pp = 0; pp < 60; pp++ ) hrest.slider[pp] = lGtex_info[pp]; hrest.hairnumber=ind; MTJsrand( ( unsigned long ) indx, gs ); hrest.hairnumber=ind; if (type==0) MTcolor_a_hair( &hrest,gs ); else MTcolor_a_hairRS( &hrest,gs ); MTJsrand( ( unsigned long ) indx, gs ); h.killme = hrest.killme; h.cutlength = hrest.cutlength; } if( hrest.killme == 0 ) { MTJsrand( (unsigned long) ind, gs ); if (type==0) h = MTgen_hair( pid, slg, gs ); if (type==1) h = MTgen_hairRS( pid, slg, gs ); cinfo->norm = h.norm; h.pid = pid; h.vid = ind; } memcpy(outhair,&hrest,sizeof(BASEHAIR)); for (x=0;x<15;x++) { outhair->pid=pid; outhair->vid=ind; outhair->hv[x]=h.hv[x]; outhair->velocity[x]=h.hv[x]; } outhair->handle=Vnorm(h.handle); outhair->norm=h.norm; outhair->clone=clone; #ifdef docache if( clone==0 ) { { memcpy( &gs->MTLOTSWFlastone, &h, sizeof( BASEHAIR ) ); memcpy( &gs->MTLOTSWFlasthrest, &hrest, sizeof( BASEHAIR ) ); memcpy( &gs->MTLOTSWFlastcinfo, cinfo, sizeof( CURVEINFO ) ); memcpy( gs->MTT2, &t2, sizeof( VERT ) * 15 ); gs->MTLOTSWFlastpid=pid; } } #endif return; } if (segs>0) { if( cinfo->killme == 0 ) #ifdef docache if( clone == 0 ) #endif if( lTotal_lSlgfaces > 0 ) if( lLCL_CNT > 0 ) if( chk ) { int lp; VERT p1p; BASEHAIR THISrest; lp = lLCL_PASSES; //lp-=1;if (lp<1) lp=1; MTJsrand( ( unsigned long ) indx, gs ); mixitup( ); killit = 0; cinfo->killme = 0; { if (lClumps>0) if (segs>0) { CURVEINFO cret; BASEHAIR genhair; int cur; MTJsrand( ( unsigned long ) ind, gs ); if (type==0) MTdraw_lotsWFROOTREST( slg, ind, offs, 0, &genhair, &cret,0, gs ); if (type==1) MTdraw_lotsWFRSROOTREST( slg, ind, offs, 0, &genhair, &cret,0, gs ); clumpCenter.randomize=genhair.randomize; clumpCenter.scruffle=genhair.scruffle; p1p=genhair.noisev[0]; MTJsrand( ( unsigned long ) ind, gs ); if (type==0) generate_clump_center(&clumpCenter,&cret,p1p,slg,gs); if (type==1) generate_clump_centerRS(&clumpCenter,&cret,p1p,slg,gs); MTJsrand( ( unsigned long ) clumpCenter.index, gs ); MTdisplace_center( &clumpCenter, clumpCenter.scruffle,gs ); } killit = 0; cinfo->killme = 0; } Gslg=slg; GhairID=ind; // GnodeID=lNodeID; // if( cachemode == PLAYBACK_CACHE ) // { // if (type==0) // hrest = MTgen_resthair_ROOT( pid, slg, cinfo, gs ); // if (type==1) // hrest = MTgen_resthair_ROOTRS( pid, slg, cinfo, gs ); // hrest.killme = 0; // cinfo->killme = 0; // killit = 0; // } // else MTJsrand( ( unsigned long ) ind, gs ); { if (type==0) hrest = MTgen_resthair( pid, slg, cinfo, gs ); if (type==1) hrest = MTgen_resthairRS( pid, slg, cinfo, gs ); } { int qq; for( qq = 0; qq < 15; qq++ ) hrest.hv[qq] = hrest.noisev[qq]; } hrest.killme = 0; cinfo->killme = 0; killit = 0; hrest.pid = pid; d1 = ascalr; d2 = ascalh; //if (ind<10) //{ //fprintf (stdout,"type = %d segs = %d lNodeID = %d lShaveID = %d TEXCACHEMODE = %d\n",type,segs,lNodeID,lShaveID, TEXCACHEMODE);fflush(stdout); //} // new if( hrest.restlength < 0.00001f ) { hrest.killme = 1; killit = 1; cinfo->killme = 1; } { int qq; for (qq=0;qq<60;qq++) hrest.slider[qq] = 1.0f; } if( TEXCACHEMODE == 1 ) { int qq; int tt = 0; float *Gtc; int gttc; lGdisplace.x=0; lGdisplace.y=0; lGdisplace.z=0; Gtc=Gtex_cacheinfo[lNodeID][slg]; gttc=Gtex_totalchannels[lNodeID][slg]; if (Gtex_cachedisplace[lNodeID][slg]) lGdisplace = Gtex_cachedisplace[lNodeID][slg][ind]; // + lCurpass * lGshavep[lNodeID].haircount[slg]]; tt=0; for( qq = 0; qq < 60; qq++ ) { lGtex_info[qq] = 1.0f; // tt = Gtex_channellink[lNodeID][qq]; if( Gtex_channellink[lNodeID][qq] >= 0 ) { lGtex_info[qq] = Gtc[tt+gttc*ind];// + lCurpass * gttc + gttc * ind * lGshavep[lNodeID].passes[slg]]; // Gtex_info[qq] = Gtc[tt + lCurpass * gttc + gttc * ind * lGshavep[lNodeID].passes[slg]]; tt++; } } } { int pp; if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 1 ) for( pp = 0; pp < 60; pp++ ) { int tt; hrest.slider[pp] = lGtex_info[pp]; } } hrest.hairnumber=ind; cinfo->depthpass=lCurpass; Gslg=slg; GhairID=ind; //GnodeID=lNodeID; MTJsrand( ( unsigned long ) indx, gs ); { hrest.hairnumber=ind; if (type==0) { MTcolor_a_hair( &hrest,gs ); } else MTcolor_a_hairRS( &hrest,gs ); } MTJsrand( ( unsigned long ) indx, gs ); if( cachemode != PLAYBACK_CACHE ) { mixitup( ); { int pp; cinfo->mtl = slg; cinfo->groupID = slg; cinfo->hairID=ind; cinfo->depthpass=0; // cinfo->depthpass=lCurpass; cinfo->mtl = slg; cinfo->groupID = slg; Gslg=slg; GhairID=ind; //GnodeID=lNodeID; //fprintf(stdout,"UTpid = %d\n",cinfo->UTpid);fflush(stdout); if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 0 ) for( pp = 0; pp < 60; pp++ ) { hrest.slider[pp] = SHAVEapply_texture( cinfo, hrest.hv[0], lShaveID, pp, hrest.slider[pp] ); } if( DOING_SWATCH == 0 ) if( TEXCACHEMODE == 1 ) for( pp = 0; pp < 60; pp++ ) { hrest.slider[pp] = lGtex_info[pp]; } hrest.hairnumber=ind; MTJsrand( ( unsigned long ) indx, gs ); // hrest.hairnumber=ind; { hrest.hairnumber=ind; if (type==0) { MTcolor_a_hair( &hrest,gs ); } else MTcolor_a_hairRS( &hrest,gs ); } MTJsrand( ( unsigned long ) indx, gs ); h.killme = hrest.killme; h.cutlength = hrest.cutlength; killit = 0; } } h.killme = hrest.killme; // if( hrest.killme == 0 ) { MTJsrand( ind, gs ); if (type==0) h = MTgen_hair( pid, slg, gs ); if (type==1) h = MTgen_hairRS( pid, slg, gs ); cinfo->norm = h.norm; h.pid = pid; h.vid = ind; } //printf("f\n");fflush(stdout); cinfo->ambient = hrest.ambient; cinfo->mtl = hrest.mtl; cinfo->restlength = hrest.restlength; cinfo->diff = hrest.diffuse; cinfo->spec = hrest.spec; cinfo->kspec = hrest.kspec; cinfo->shad = hrest.ambient; cinfo->killme = hrest.killme; cinfo->cutlength = hrest.cutlength; cinfo->baserad = 2.0f * hrest.slider[20] * hrest.thickness * lRestBOUNDLENGTH / 1500.0; cinfo->tiprad = 2.0f * hrest.slider[37] * hrest.thicknesstip * lRestBOUNDLENGTH / 1500.0; if (segs>0) if( hrest.killme == 0 ) { int qq; VERT displace, root; root = h.hv[0]; SHAVEcoord_convertFROMSHAVE( &root ); root.z *= -1.0f; displace.x = 0; displace.y = 0; displace.z = 0; if( TEXCACHEMODE == 1 ) displace = lGdisplace; if( TEXCACHEMODE == 0 ) if( DOING_SWATCH == 0 ) displace = SHAVEdisplace_root( &root, cinfo, lShaveID ); SHAVEcoord_convertFROMSHAVE( &hrest.norm ); hrest.norm.z *= -1.0f; if( TEXCACHEMODE == 1 ) displace = lGdisplace; // ok lets cache this value if( lSlidervals[43] != 0.0f ) { displace.x += hrest.slider[43] * hrest.norm.x * lSlidervals[43]; displace.y += hrest.slider[43] * hrest.norm.y * lSlidervals[43]; displace.z += hrest.slider[43] * hrest.norm.z * lSlidervals[43]; } hrest.norm.z *= -1.0f; SHAVEcoord_convertTOSHAVE( &hrest.norm ); h.clump_strength = ( float ) hrest.clump_strength; h.clump_color_strength = (float) hrest.clump_color_strength; h.clump_rot_strength = (float) hrest.clump_rot_strength; h.clump_rot_offset = (float) hrest.clump_rot_offset; h.clump_flatness = (float) hrest.clump_flatness; h.randomize = (float) hrest.randomize; //if (0==1) if (segs>0) if (lClumps>0) { MTJsrand(indx,gs); displace_scale( &clumpCenter, hrest.slider[41] * lSlidervals[41] ); clumpCenter.randscale = hrest.slider[36] * lSlidervals[36]; MTJsrand( ( unsigned long ) ( indx ), gs ); if (segs>0) if( hrest.killme == 0 ) MTdisplace_randscale( &clumpCenter, gs ); // extra scale //if (segs>0) // bug fix - shouldn't be scaling twice if( hrest.killme == 0 ) // displace_scale( &clumpCenter, hrest.slider[41] * lSlidervals[41] ); for (qv=1;qv<15;qv++) // apply clumps (test) { float ii,aa,bb; float flat; float ccl; ii=((float)qv/14.0f); aa=(1.0-ii); bb=(ii); flat=1.0f-h.clump_flatness; ccl=h.clump_strength*flat; h.hv[qv].x=(h.hv[qv].x*aa+clumpCenter.hv[qv].x*bb)*ccl+h.hv[qv].x*(1.0-ccl); h.hv[qv].y=(h.hv[qv].y*aa+clumpCenter.hv[qv].y*bb)*ccl+h.hv[qv].y*(1.0-ccl); h.hv[qv].z=(h.hv[qv].z*aa+clumpCenter.hv[qv].z*bb)*ccl+h.hv[qv].z*(1.0-ccl); // h.hv[qv]=clumpCenter.hv[qv]; // lets look at the center } twist_basehair (&clumpCenter,&h,gs); } MTJsrand( indx, gs ); SHAVEcoord_convertTOSHAVE( &displace ); displace.z *= -1.0f; for( qq = 0; qq < 15; qq++ ) { h.hv[qq].x += displace.x; h.hv[qq].y += displace.y; h.hv[qq].z += displace.z; if (lClumps>0) { clumpCenter.hv[qq].x+= displace.x; clumpCenter.hv[qq].y+= displace.y; clumpCenter.hv[qq].z+= displace.z; } } } if( cachemode != PLAYBACK_CACHE ) if( hrest.killme == 0 ) for( qv = 0; qv < 15; qv++ ) { Matrix tmpmat; int q1, q2, qb, q3, q4; VERT yv2; { b.x = 0; b.y = ( ( float ) qv / 15.0f ) * h.restlength * 15.0f; b.z = 0; t1[qv] = b; } } if (segs>0) { BASEMATRIX bm; make_basematrix(&h,&bm); for (qv=0;qv<15;qv++) { t2[qv]=h.hv[qv]; memcpy(&cinfo->zero2world[qv],&bm.zero2world,sizeof(Matrix)); } } //printf("8\n");fflush(stdout); h.killme = hrest.killme; h.slgroup = hrest.slgroup; h.pid = pid; h.multasp=hrest.multasp; h.aspect=hrest.aspect; h.offset=hrest.offset; h.randscale = hrest.randscale; h.dreadroot = hrest.dreadroot; h.dreadtip = hrest.dreadtip; h.dreadcount = hrest.dreadcount; h.kink = hrest.kink; h.kinkfreq = hrest.kinkfreq; h.rootfrizz = hrest.rootfrizz; h.tipfrizz = hrest.tipfrizz; h.clumpfreq = hrest.clumpfreq; h.spec = hrest.spec; h.kspec = hrest.kspec; h.diffuse = hrest.diffuse; h.ambient = hrest.ambient; h.thickness = hrest.thickness; h.thicknesstip = hrest.thicknesstip; h.uu = hrest.uu; h.vv = hrest.vv; h.cutlength = hrest.cutlength; h.slgroup = hrest.slgroup; h.restlength = hrest.restlength; h.slgroup = hrest.slgroup; h.mess= hrest.mess; h.flyaway = hrest.flyaway; h.clump_strength=hrest.clump_strength; h.clump_color_strength = (float) hrest.clump_color_strength; h.clump_rot_strength = hrest.clump_rot_strength; h.frizzfreqX=hrest.frizzfreqX; h.frizzfreqY=hrest.frizzfreqY; h.frizzfreqZ=hrest.frizzfreqZ; h.kinkfreqX=hrest.kinkfreqX; h.kinkfreqY=hrest.kinkfreqY; h.kinkfreqZ=hrest.kinkfreqZ; h.cutlength=hrest.cutlength; for( qv = 0; qv < 15; qv++ ) { h.color[qv] = hrest.color[qv]; h.noisev[qv] = hrest.noisev[qv]; } //printf("7\n");fflush(stdout); // if (0==1) if (segs>0) if( cachemode != PLAYBACK_CACHE ) if( hrest.restlength > 0.0f ) // new if( hrest.killme == 0 ) if( !killit ) { BASEHAIR ttt; memcpy( &ttt, &hrest, sizeof( BASEHAIR ) ); for( qv = 0; qv < 15; qv++ ) ttt.hv[qv] = t1[qv]; for( qv = 0; qv < 15; qv++ ) ttt.velocity[qv] = h.velocity[qv]; ttt.pid = h.pid; ttt.vid = h.vid; ttt.restlength = hrest.restlength; MTJsrand( indx, gs ); //if (qq2>0) if (segs>0) { MTdisplace_kinky( &ttt, ttt.kink * .1f, ttt.kinkfreq * .05f, gs ); ttt.clone=clone; MTJsrand( indx, gs ); MTdisplace_clumpy( &ttt, gs ); } memcpy( &hrest, &ttt, sizeof( BASEHAIR ) ); } if( !killit ) if( hrest.killme == 0 ) { if( cachemode != PLAYBACK_CACHE ) for( qv = 0; qv < 15; qv++ ) { { h.hv[qv].x = ( hrest.hv[qv].x - t1[qv].x ); h.hv[qv].y = ( hrest.hv[qv].y - t1[qv].y ); h.hv[qv].z = ( hrest.hv[qv].z - t1[qv].z ); h.color[qv] = hrest.color[qv]; } } if( cachemode == CREATE_CACHE ) h.pid = pid; if( cachemode == CREATE_CACHE ) add_cache( &h, cinfo ); if( cachemode == PLAYBACK_CACHE ) fetch_cache( &h, cinfo ); } } if( chk == 0 ) { int xx; //printf("9\n");fflush(stdout); for( xx = 1; xx < 15; xx++ ) { h.hv[xx] = h.hv[0]; h.resthv[xx] = h.resthv[0]; } } // if (cloneON==0) #ifdef docache if (segs>0) if( clone==0 ) { { memcpy( &gs->MTLOTSWFlastone, &h, sizeof( BASEHAIR ) ); memcpy( &gs->MTLOTSWFlasthrest, &hrest, sizeof( BASEHAIR ) ); memcpy( &gs->MTLOTSWFlastcinfo, cinfo, sizeof( CURVEINFO ) ); memcpy( gs->MTT2, &t2, sizeof( VERT ) * 15 ); gs->MTLOTSWFlastpid=pid; } } #endif if( chk ) if( hrest.killme == 0 ) for( qv = 0; qv < 15; qv++ ) { { if (segs>0) { float rad; rad = hrest.thickness * ( ( float ) ( 15 - qv ) / 15.0 ); rad += hrest.thicknesstip * ( ( float ) ( qv ) / 15.0 ); rad *= lRestBOUNDLENGTH / 1500.0f; rad *= 1.5f; h.hv[qv].x += offs.x * rad; h.hv[qv].y += offs.y * rad; h.hv[qv].z += offs.z * rad; } if (segs>0) h.hv[qv] = vxm( cinfo->zero2world[qv], h.hv[qv] ); h.hv[qv].x += t2[qv].x; h.hv[qv].y += t2[qv].y; h.hv[qv].z += t2[qv].z; } } // MTJsrand( ( unsigned long ) ( qv * 61 + qv ), gs ); // h.randscale = lSlidervals[36] * hrest.slider[36]; MTJsrand( ( unsigned long ) ( indx ), gs ); if (segs>0) if( chk ) if( hrest.killme == 0 ) MTdisplace_randscale( &h, gs ); if (segs>0) if( chk ) if( hrest.killme == 0 ) if( cloneON > 0 ) { float ss; ss=(float) MTdrand98(gs); if (ss<.5) ss= -1.0f; if (ss>=.5) ss= 1.0f; MTJsrand( ( unsigned long ) ( indx ), gs ); if (segs>0) { float ang, mag; Matrix pm; BASEMATRIX bm; VERT foffs; float sc = 0.0f; int ll = 0; mixitup( ); { ang= (float) (MTdrand98( gs )*6.284-3.14); mag= (float) MTdrand98(gs); foffs.x=cos(ang)*mag; foffs.z=sin(ang)*mag; } // foffs.z*=h.multasp; // aspect // foffs.y = 0; make_basematrix(&h,&bm); // if( slg != 4 ) // { // if (type==0) // mk_polymat2( pm, pid,h.handle ); // threaded? // if (type==1) // mk_polymatRS2( pm, pid,h.handle,gs ); // threaded? // } // if( slg == 4 ) // { // if (type==0) // Smk_polymat( pm, pid ); // threaded? // if (type==1) // Smk_polymatRS( pm, pid,gs ); // threaded? // } // foffs = vxm( pm, foffs ); // this should really be on a per segment basis - and we can rotate it. foffs = vxm( pm, foffs ); // this should really be on a per segment basis - and we can rotate it. if( slg != 4 ) sc = hrest.restlength * 6.0f; if( slg == 4 ) sc = hrest.restlength / 5.0f; // * 6.0f; foffs.x *= sc; foffs.y *= sc; foffs.z *= sc; ang= (float) (MTdrand98( gs )*6.284-3.14); mag= (float) MTdrand98(gs); for( ll = 0; ll < 15; ll++ ) { float vs; float ivs; vs = ( float ) ll / 15.0f; if( vs > 1.0 ) vs = 1.0f; ivs = ( float ) ll / 15.0f; if( ivs > 1.0 ) ivs = 1.0f; ivs = 1.0f - ivs; vs = ( ( float ) vs ) * h.dreadtip + ( ivs ) * h.dreadroot; if( vs < 0 ) vs = 0; { float ang2; float offx; float uu; uu=(float)ll/15.0f; uu=pow(uu,1.25); ang2=uu*1.28f*h.multasp*ss; foffs.x=cos(ang)*mag*vs*sc; foffs.z=sin(ang)*mag*vs*sc; foffs.z*=h.aspect; foffs.x+=(h.offset*sc*uu); foffs.y=0; { VERT off2; off2=foffs; foffs.x=off2.x*cos(ang2)-off2.z*sin(ang2); foffs.z=off2.x*sin(ang2)+off2.z*cos(ang2); } foffs = vxm( bm.zero2world[ll], foffs ); } h.hv[ll].x += foffs.x;// * vs; h.hv[ll].y += foffs.y;// * vs; h.hv[ll].z += foffs.z;// * vs; } MTJsrand( ( unsigned long ) ( indx ), gs ); } } cinfo->killme = hrest.killme; // Don't zero out killed hairs? if( hrest.killme == 1 ) // { // int qq; // // VERT zero; // zero=h.hv[0]; // for( qq = 0; qq < 15; qq++ ) // { // // // h.hv[qq] = zero; // } // } h.randscale = hrest.randscale; mixitup( ); //if(lClumps==0) if (segs>0) if( hrest.killme == 0 ) displace_scale( &h, hrest.slider[41] * lSlidervals[41] ); if (segs>0) // come back here if( h.killme == 0 )// come back here if( chk ) if( cloneON > 0 ) { float oldr; oldr = h.randscale; h.randscale = hrest.slider[42] * lSlidervals[42]; if (segs>0) MTJsrand( ( unsigned long ) (indx ), gs ); mixitup( ); hrest.hairnumber=ind; MTJsrand( ( unsigned long ) ( indx ), gs ); if (segs>0) if( hrest.killme == 0 ) MTdisplace_randscale( &h, gs ); // extra scale MTJsrand( ( unsigned long ) ( indx ), gs ); { h.hairnumber=ind; if (type==0) { MTcolor_a_hair( &h,gs ); } else MTcolor_a_hairRS( &h,gs ); if( h.killme == 0 ) if( h.cutlength < (1.0f/(float)segs) ) { cinfo->killme=1; h.killme = 1; } MTJsrand( ( unsigned long ) indx, gs ); } for( pd = 0; pd < 15; pd++ ) h.color[pd] = hrest.color[pd]; h.randscale = oldr; } // if (segs>0) // MTJsrand( ( unsigned long ) (indx ), gs ); mixitup( ); hrest.hairnumber=ind; hrest.index=ind; h.randscale = hrest.slider[36] * lSlidervals[36]; MTJsrand( ( unsigned long ) ( indx ), gs ); if (segs>0) if( hrest.killme == 0 ) MTdisplace_randscale( &h, gs ); // extra scale MTJsrand( ( unsigned long ) ( indx ), gs ); h.clump_color_strength=hrest.clump_color_strength; if (hrest.killme==0) if (segs>0) if (lClumps>0) MTcolor_clumpy( &h,&clumpCenter, gs ); cinfo->depthpass=lCurpass; //cinfo->maxpasses=lLCL_PASSES[slg]; } // if( hrest.killme == 0 ) memcpy( outhair, &h, sizeof( BASEHAIR ) ); outhair->dreadcount=hrest.dreadcount; if (h.killme) { outhair->killme = 1; cinfo->killme = 1; } outhair->index=ind; outhair->cutlength=hrest.cutlength; if (h.cutlength< (1.0f/(float)segs) ) { outhair->killme=1; cinfo->killme=1; } if( h.restlength < 0.0001f ) { outhair->killme = 1; cinfo->killme = 1; } if (outhair->killme) { for (x=1;x<15;x++) outhair->hv[x]=outhair->hv[0]; } cinfo->norm.z*= -1.0f; cinfo->hairID=ind; cinfo->spec_tint=spec_tint; cinfo->spec_tint2=spec_tint2; // if (ind<90) printf ("MTdraw_lotsWF %d dreadcount=%d sliders[25][0].value = %f outhair->slider[25]=%f\n",ind,outhair->dreadcount,sliders[25][0].value,outhair->slider[25]); } static void matrot( Matrix mat, /* resulting rotation matrix (out) */ VERT vrot /* rotation vector (in) */ ); static void rotx( Matrix mat, /* resulting rotation matrix (out) */ float vrot /* rotation vector (in) */ ); static void roty( Matrix mat, /* resulting rotation matrix (out) */ float vrot /* rotation vector (in) */ ); static void rotz( Matrix mat, /* resulting rotation matrix (out) */ float vrot /* rotation vector (in) */ ); float getnoise_octaves(float x, float y, float z, int octaves) { int xx; float accum=0.0f; float amp=1.0f; x/=2.0f; y/=2.0f ; z/=2.0f; for (xx=0;xxtipfrizz*0.8f; //fly=h->its_flyaway; { float wt; for (xx=0;xx<20*MTdrand98(gs)+10;xx++) wt=MTdrand98(gs); if (wtrs.Gflyaway_percent) { fly=1; tfrizz/=2.0f; } else fly=0; } //MTJsrand(gs->lastSeed,gs); // reset the randome gen freqqx = h->frizzfreqX * 0.5f; freqqy = h->frizzfreqY * 0.5f; freqqz = h->frizzfreqZ * 0.5f; // if (h->frizzanim!=0.0f) // fprintf(stdout,"frizzanim %f frizzanimspeed %f frizzanimdir %f %f %f\n",h->frizzanim,h->animspeed,h->animdir.x,h->animdir.y,h->animdir.z);fflush(stdout); if (h->frizzanim>0.0f||(h->rootfrizz>0.0f)||h->tipfrizz>0.0f||h->flyaway>0.0f||h->mess>0.0f) // if (freqq>0) { VERT offs; dv.x = 0; dv.y = 0; dv.z = 0; { amp = 1.0f; freqx = freqqx / ( h->restlength * 111.1f ); freqy = freqqy / ( h->restlength * 111.1f ); freqz = freqqz / ( h->restlength * 111.1f ); if( h->slgroup == 4 ) freqx *= 56.0f; if( h->slgroup == 4 ) freqy *= 56.0f; if( h->slgroup == 4 ) freqz *= 56.0f; #ifdef EXTERNAL_COLLISION // backwards compatibility if( h->slgroup != 4 ) { freqx *= 5.0f; freqy *= 5.0f; freqz *= 5.0f; // unlock } #endif h->animdir.x = 0.0f; // need to follow up on this h->animdir.y = 1.0f; h->animdir.z = 0.0f; offs.x = 0; offs.y = 0; offs.z = 0; dv.x = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x+.4, h->noisev[pp].y*freqy+offs.y, h->noisev[pp].z*freqz+offs.z-0.5f,3)); dv.y = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x+0.1f, h->noisev[pp].y*freqy+offs.y+0.5f, h->noisev[pp].z*freqz+offs.z-0.2f,3)); dv.z = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x, h->noisev[pp].y*freqy+offs.y-0.1f, h->noisev[pp].z*freqz+offs.z+0.5f,3)); dv.x *= ( 1.0 - h->frizzanim ); dv.y *= ( 1.0 - h->frizzanim ); dv.z *= ( 1.0 - h->frizzanim ); h->animdir.x = 0.0f; h->animdir.y = 1.0f; h->animdir.z = 0.0f; offs.x = gt * freqx * h->animspeed * h->animdir.x; offs.y = gt * freqy * h->animspeed * h->animdir.y; offs.z = gt * freqz * h->animspeed * h->animdir.z; dv2.x = 0; dv2.y = 0; dv2.z = 0; if( h->frizzanim != 0.0f ) { dv2.x = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x+.9, h->noisev[pp].y*freqy+offs.y-.2, h->noisev[pp].z*freqz+offs.z-.1,2)); dv2.y = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x+0.11f, h->noisev[pp].y*freqy+offs.y+0.15f, h->noisev[pp].z*freqz+offs.z-0.62f,2)); dv2.z = (getnoise_octaves(h->noisev[pp].x*freqx+offs.x, h->noisev[pp].y*freqy+offs.y-0.71f, h->noisev[pp].z*freqz+offs.z+0.25f,2)); dv2.x *= h->frizzanim; dv2.y *= h->frizzanim; dv2.z *= h->frizzanim; } } dv.x += dv2.x; dv.y += dv2.y; dv.z += dv2.z; dv.x = dv.x + ( dv.x * .05 ) * MTdrand98( gs ); dv.y = dv.y + ( dv.y * .05 ) * MTdrand98( gs ); dv.z = dv.z + ( dv.z * .05 ) * MTdrand98( gs ); dv.x = -dv.x; dv.y = -dv.y; dv.z = -dv.z; if ((h->rootfrizz>0.0f)||h->tipfrizz>0.0f||h->flyaway>0.0f||h->mess>0.0f) { float st; VERT dv3; float rff, tff; // tip frizz and root frizz are reversed rff = h->rootfrizz / 6.0f; tff = tfrizz / 6.0f; if( h->slgroup == 4 ) { rff /= 4.0f; tff /= 4.0f; } for( xx = 1; xx < 15; xx++ ) { float qq; qq = ( xx / ( float ) 15.0f ); qq = ( ( float ) 1.0 - qq ) * rff + qq * tff; qq *= ( float ) 3.14 / ( float ) 180.0f; qq *= ( float ) .5; //if (h->its_flyaway) if (fly) qq += h->flyaway*10.0f*( xx / ( float ) 15.0f ); { rotx( mr1, dv.x*qq ); // load up rotation matricies rotz( mr3, dv.z*qq ); roty( mr2, dv.y*qq ); } h->hv[xx].x -= h->hv[0].x; // bring it back to origin h->hv[xx].y -= h->hv[0].y; h->hv[xx].z -= h->hv[0].z; h->hv[xx] = vxm( mr1, h->hv[xx] ); h->hv[xx] = vxm( mr3, h->hv[xx] ); h->hv[xx] = vxm( mr2, h->hv[xx] ); if (fly) { float hm; float ddx,ddy,ddz; hm=h->mess*h->restlength; h->hv[xx].x+=hm*(MTdrand98(gs)-0.5f); // mess h->hv[xx].y+=hm*(MTdrand98(gs)-0.5f); h->hv[xx].z+=hm*(MTdrand98(gs)-0.5f); { h->hv[xx].x*=(1.0f+h->flyaway*1.2f); // flyaway - multipliers h->hv[xx].y*=(1.0f+h->flyaway*1.2f); h->hv[xx].z*=(1.0f+h->flyaway*1.2f); } } h->hv[xx].x += h->hv[0].x; h->hv[xx].y += h->hv[0].y; h->hv[xx].z += h->hv[0].z; } } } } static void MTdisplace_center( BASEHAIR * h, float amp,GLOBS * gs ) { int xx; float freqqx, freqqy, freqqz; VERT dv; VERT dv2; Matrix mr1, mr2, mr3; float freqx, freqy, freqz; int oc; float wt; int fly=0; int pp=0; float tfrizz; tfrizz=amp; //MTJsrand(gs->lastSeed,gs); // reset the randome gen freqqx = h->frizzfreqX * 0.5f; freqqy = h->frizzfreqY * 0.5f; freqqz = h->frizzfreqZ * 0.5f; // if (h->frizzanim!=0.0f) if (amp>0.0f) // if (freqq>0) { VERT offs; dv.x = 0; dv.y = 0; dv.z = 0; dv.x=MTdrand98(gs)-.5f; dv.y=MTdrand98(gs)-.5f; dv.z=MTdrand98(gs)-.5f; if (amp>0.0f) { float st; VERT dv3; float rff, tff; // tip frizz and root frizz are reversed rff=0; tff = amp; if( h->slgroup == 4 ) { rff /= 4.0f; tff /= 4.0f; } for( xx = 1; xx < 15; xx++ ) { float qq; qq = ( xx / ( float ) 15.0f ); qq = ( ( float ) 1.0 - qq ) * rff + qq * tff; qq *= ( float ) 3.14 / ( float ) 180.0f; qq *= ( float ) .5; { rotx( mr1, dv.x*qq ); // load up rotation matricies rotz( mr3, dv.z*qq ); roty( mr2, dv.y*qq ); } h->hv[xx].x -= h->hv[0].x; // bring it back to origin h->hv[xx].y -= h->hv[0].y; h->hv[xx].z -= h->hv[0].z; h->hv[xx] = vxm( mr1, h->hv[xx] ); h->hv[xx] = vxm( mr3, h->hv[xx] ); h->hv[xx] = vxm( mr2, h->hv[xx] ); h->hv[xx].x += h->hv[0].x; h->hv[xx].y += h->hv[0].y; h->hv[xx].z += h->hv[0].z; } } } }