Hair export to Mitsuba

PhilemoPhilemo Posts: 885
edited November 2015 in Carrara SDK Developer Discussion

Following a request from PhilW and for those who are interested, this is how I handled the hair export from Carrara to Mitsuba :
In Carrara, the Hair primitive exports segments as renderable for preview in the assembly room. The shaping done in the shader (frizz and so one) are kept.

Exporting from Carrara

ptree CarraraSceneParser::createHair(I3DShInstance * instance){
 ptree result;
 TMCCountedPtr shObject;
 instance->Get3DObject(&shObject;);
 const TRenderableAndTfmArray * renderables;
 instance->GetTreeElement()->BeginGetRenderables(renderables,false);
 instance->GetTreeElement()->EndGetRenderables();
 std::vector segments;
 std::vector points;
 for (uint32 i = 0; i < renderables->GetElemCount(); i++){
  TRenderableAndTfm rf;
  renderables->GetElem(i,rf);
  I3DShRenderable::EType t =  rf.fRenderable->GetGeometryType();
  if (t == I3DShRenderable::EType::kType_Segment) {
   const TSegmentMesh * segment = rf.fRenderable->GetSegmentMesh();
   for (uint32 j = 0; j < segment->fVertex.GetElemCount(); j++) {
    TVector3 vec = segment->fVertex[j];
    points.push_back (MinVec3(vec.x,vec.y,vec.z));
   }
   for (uint32 j = 0; j < segment->fSegmentIndices.GetElemCount(); j++) {
    TIndex2 extr = segment->fSegmentIndices[j];
    segments.push_back(MinVec2I(extr.x,extr.y));
   }


  }

 }
 TMCDynamicString name;
 shObject->GetName(name);
 std::string meshName = name.StrGet();
 meshName = "M-" + meshName;
 std::string filename;
 if (masters.find(meshName) == masters.end()) {


  TR("serializing %s %s",path.c_str(),meshName.c_str());
  MultiThreadEndJobNotifier * notifier = multiThreadManager->createNotifier();
  multiThreadManager->acquire();
  filename = GlueUtils::getInstance()->serializeHair(path,meshName,points,segments,notifier);

 

Mitsuba hair file format is quite simple: from the documentation

There is also a binary format, which starts with the identifier “BINARY_HAIR” (11 bytes), followedby the number of vertices as a 32-bit little endian integer. The remainder of the file consists of thevertex positions stored as single-precision XYZ coordinates (again in little-endian byte ordering). Tomark the beginning of a new hair strand, a single +8 floating point value can be inserted betweenthe vertex data.


Serializing for Mitsuba

 

void HairSerializer::run() {
 boost::filesystem::path pa = boost::filesystem::path(path);
 pa /= boost::filesystem::path(name);
 ref fs = new FileStream(pa,mitsuba::FileStream::ETruncWrite);
 fs->setByteOrder(mitsuba::FileStream::ELittleEndian);
 int nbHairs = 0;
 int prev = -1;
 for (int i = 0; i < segments.size(); i++) {
  if (segments[ i ].x != prev) 
   nbHairs++;
  prev = segments[ i ].y;
 }

 const char *binaryHeader = "BINARY_HAIR";
 fs->write(binaryHeader,11);
 fs->writeUInt(((int)segments.size())+nbHairs);

 bool notFirst = false;
 prev = -1;
 for (int i = 0; i < segments.size(); i++) {
  if (segments[ i ].x != prev) {
   if (notFirst) {
    fs->writeFloat(std::numeric_limits::infinity());
   }
   fs->writeFloat(points[segments[ i ].x].x);
   fs->writeFloat(points[segments[ i ].x].y);
   fs->writeFloat(points[segments[ i ].x].z);

  }
  fs->writeFloat(points[segments[ i ].y].x);
  fs->writeFloat(points[segments[ i ].y].y);
  fs->writeFloat(points[segments[ i ].y].z);
  prev = segments[ i ].y;
  notFirst = true;

 }

 fs->close();

 notified->ended();
}

 

 

Post edited by Richard Haseltine on
Sign In or Register to comment.