// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 /********************************************************************** *< FILE: shavePack2TexCmd.cpp DESCRIPTION: command for packing hair data to image file HISTORY: created 23-04-2013 *> **********************************************************************/ #include "shavePack2TexCmd.h" #include "shaveHairShape.h" #include "shaveAPI.h" #include "shaveRender.h" #include #include #include #include #include #include #include #include #include /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ | Command | /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ MString shavePack2TexCmd::cmd = "shavePack2Tex"; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ | Flags | /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ char* shavePack2TexCmd::sf_file = "f"; char* shavePack2TexCmd::lf_file = "file"; char* shavePack2TexCmd::sf_hair = "h"; char* shavePack2TexCmd::lf_hair = "hair"; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ | Methods | /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ MStatus shavePack2TexCmd::doIt(const MArgList &maList) { MStatus stat; MArgDatabase parser(syntax(),maList,&stat); if(stat != MStatus::kSuccess) { MGlobal::displayError("shavePack2TexCmd: can not parse arguments."); return stat; } if(parser.isFlagSet(sf_file) && parser.isFlagSet(sf_hair)) { MString file; MString hair; if(parser.getFlagArgument(sf_file,0,file) != MStatus::kSuccess) { MGlobal::displayError("shavePack2TexCmd: can not get 'file' argument."); return MStatus::kFailure; } MStringArray fparts; if(file.split('.',fparts) != MStatus::kSuccess || fparts.length() < 2) { MGlobal::displayError(MString("shavePack2TexCmd: can not get file extention from ") + file); return MStatus::kFailure; } if(fparts[1] != "tga") { MGlobal::displayWarning("shavePack2TexCmd: you need 32bpp format without compression .tga"); return MStatus::kFailure; } if(parser.getFlagArgument(sf_hair,0,hair) != MStatus::kSuccess) { MGlobal::displayError("shavePack2TexCmd: can not get 'hair' argument."); return MStatus::kFailure; } MObject node; if(!findNodeByName(hair,node)) { MGlobal::displayError(MString("shavePack2TexCmd: can not find ") + hair + " node"); return MStatus::kFailure; } MFnDependencyNode dFn(node); if(dFn.typeId() != shaveHairShape::id) { MGlobal::displayError(MString("shavePack2TexCmd: the node ") + hair + " is not a shaveHairShape"); return MStatus::kFailure; } MImage img; if(!copyToImage(node,img)) { MGlobal::displayError("shavePack2TexCmd: can not pack to image."); return MStatus::kFailure; } MString mdir; MGlobal::executeCommand("internalVar -userWorkspaceDir",mdir); MString out = mdir+file; MGlobal::displayInfo(MString("shavePack2TexCmd: saving file ") + out ); if(img.writeToFile(out,fparts[1]) != MStatus::kSuccess) { MGlobal::displayError(MString("shavePack2TexCmd: can not write ") + out ); return MStatus::kFailure; } return MStatus::kSuccess; } else { MGlobal::displayError("shavePack2TexCmd: you should specify 'file' and 'hair' flags."); return MStatus::kFailure; } } MSyntax shavePack2TexCmd::newSyntax() { MSyntax syn; syn.addFlag(sf_file, lf_file, MSyntax::kString); syn.addFlag(sf_hair, lf_hair, MSyntax::kString); return syn; } #define SIMPLE_PACK static void i2c(unsigned char* c, unsigned int i) { #ifdef SIMPLE_PACK memcpy(c,&i,4); #else char b[4] = {0,0,0,0}; b[3] = i/1000000; i -= b[3]*1000000; b[2] = i/10000; i -= b[2]*10000; b[1] = i/100; i -= b[1]*100; b[0] = i; memcpy(c,b,4); #endif } static void f2c(unsigned char* c, float f) { #ifdef SIMPLE_PACK memcpy(c,&f,4); #else char sign = f >= 0.0f ? 1 : 0; f = fabs(f); float af = floor(f); float df = f - af; if(df < 0.01f) df = 0.0f; //int d = f * pow(10.0f,7) - a * pow(10.0f,7); //while(d >= 100) // d /= 10; int a = (int)af; int d = (int)(df*100.0f); char b[4] = {0,0,0,0}; b[3]=(char)(a/256); b[2]=a%256; b[1]=d; b[0]=sign; memcpy(c,b,4); #endif } bool shavePack2TexCmd::copyToImage(MObject node, MImage& image) const { //MObjectArray nodes; //nodes.append(node); //crap, unresolved!! //shaveAPI::HairInfo h; //shaveAPI::exportHair(nodes, &h); //MFnDependencyNode dFn(node); //shaveHairShape* hairShape = (shaveHairShape*)dFn.userNode(); //SHAVENODE* hairNode = hairShape->getHairNode(); //hairShape->setStackIndex(0); //SHAVEclear_stack(); //SHAVEadd_hairOPEN2(hairNode, 0); MObjectArray nodes; nodes.append(node); shaveRender::buildHairStack(nodes, shaveConstant::kShutterBoth); HAIRTYPE h; SHAVEinit_hairtype(&h); SHAVEexport_hairtype(&h); // if(h.numHairs == 0 || h.numVertices == 0 || h.numHairVertices == 0) // return false; if(h.totalfaces == 0 || h.totalverts == 0 || h.totalfverts == 0) { MGlobal::displayError("shavePack2TexCmd: hairtype is empty."); return false; } struct header { unsigned char num_hairs[4]; unsigned char num_knots[4]; }; struct hair { unsigned char start_vert[4]; unsigned char tip_radi[4]; unsigned char root_radi[4]; unsigned char root_color_r[4]; unsigned char root_color_g[4]; unsigned char root_color_b[4]; unsigned char tip_color_r[4]; unsigned char tip_color_g[4]; unsigned char tip_color_b[4]; }; struct vertex { unsigned char x[4]; unsigned char y[4]; unsigned char z[4]; }; //[header][hairs array][verts arra] //int numknots = h.hairEndIndices[0] - h.hairStartIndices[0]; int numknots = h.face_end[0] - h.face_start[0]; int numhairs = h.totalfaces; header he; //i2c(he.num_hairs,h.numHairs); i2c(he.num_hairs,h.totalfaces); i2c(he.num_knots,numknots); hair* har = (hair*) malloc(numhairs*sizeof(hair)); vertex* var = (vertex*) malloc(numhairs*numknots*sizeof(vertex)); for(int i = 0; i < numhairs; i++) { hair& hh = har[i]; i2c(hh.start_vert,i*numknots); //f2c(hh.tip_radi,h.tipRadii[i]); //f2c(hh.root_radi,h.rootRadii[i]); f2c(hh.tip_radi,h.radiustip[i]); f2c(hh.root_radi,h.radiusroot[i]); //f2c(hh.root_color_r,h.rootColors[i].r); //f2c(hh.root_color_g,h.rootColors[i].g); //f2c(hh.root_color_b,h.rootColors[i].b); f2c(hh.root_color_r,h.colorroot[i].x); f2c(hh.root_color_g,h.colorroot[i].y); f2c(hh.root_color_b,h.colorroot[i].z); //f2c(hh.tip_color_r,h.tipColors[i].r); //f2c(hh.tip_color_g,h.tipColors[i].g); //f2c(hh.tip_color_b,h.tipColors[i].b); f2c(hh.tip_color_r,h.colortip[i].x); f2c(hh.tip_color_g,h.colortip[i].y); f2c(hh.tip_color_b,h.colortip[i].z); for(int k = 0; k < numknots; k++) { //int vi = h.hairStartIndices[i] + k; int vi = h.face_start[i] + k; vertex& vv = var[i*numknots + k]; //f2c(vv.x, h.vertices[vi].x); //f2c(vv.y, h.vertices[vi].y); //f2c(vv.z, h.vertices[vi].z); f2c(vv.x, h.v[vi].x); f2c(vv.y, h.v[vi].y); f2c(vv.z, h.v[vi].z); } } int npix = (sizeof(header) + numhairs*sizeof(hair) +numhairs*numknots*sizeof(vertex))/4; int W = 1024; int H = 1 + npix/W; image.create(W,H,4); unsigned char* pix = image.pixels(); memcpy(pix,&he,sizeof(header)); memcpy(&pix[sizeof(header)],har,numhairs*sizeof(hair)); memcpy(&pix[sizeof(header)+ numhairs*sizeof(hair)],var,numhairs*numknots*sizeof(vertex)); free(har); free(var); return true; } bool shavePack2TexCmd::findNodeByName(const MString& name, MObject& thenode) const { if(name == "") return false; MItDependencyNodes iter; for (; !iter.isDone(); iter.next()) { MObject node = iter.item(); MFnDependencyNode dFn(node); if(dFn.name() == name) { thenode = node; return true; } } return false; }