// // Copyright 2013 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef SHAPE_UTILS_H #define SHAPE_UTILS_H #ifndef HBR_ADAPTIVE #define HBR_ADAPTIVE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------------------ static char const * sgets( char * s, int size, char ** stream ) { for (int i=0; i intargs; std::vector floatargs; std::vector stringargs; }; static shape * parseShape(char const * shapestr, int axis=1); std::string genShape(char const * name) const; std::string genObj(char const * name) const; std::string genRIB() const; ~shape(); int getNverts() const { return (int)verts.size()/3; } int getNfaces() const { return (int)nvertsPerFace.size(); } bool hasUV() const { return not (uvs.empty() or faceuvs.empty()); } std::vector verts; std::vector uvs; std::vector normals; std::vector nvertsPerFace; std::vector faceverts; std::vector faceuvs; std::vector facenormals; std::vector tags; Scheme scheme; }; //------------------------------------------------------------------------------ shape::~shape() { for (int i=0; i<(int)tags.size(); ++i) delete tags[i]; } //------------------------------------------------------------------------------ shape::tag * shape::tag::parseTag(char const * line) { tag * t = 0; const char* cp = &line[2]; char name[50]; while (*cp == ' ') cp++; if (sscanf(cp, "%s", name )!=1) return t; while (*cp && *cp != ' ') cp++; int nints=0, nfloats=0, nstrings=0; while (*cp == ' ') cp++; if (sscanf(cp, "%d/%d/%d", &nints, &nfloats, &nstrings)!=3) return t; while (*cp && *cp != ' ') cp++; std::vector intargs; for (int i=0; i floatargs; for (int i=0; i stringargs; for (int i=0; iname = name; t->intargs = intargs; t->floatargs = floatargs; t->stringargs = stringargs; return t; } //------------------------------------------------------------------------------ std::string shape::tag::genTag() const { std::stringstream t; t<<"\"t \""<(t)); t<<" "; std::copy(floatargs.begin(), floatargs.end(), std::ostream_iterator(t)); t<<" "; std::copy(stringargs.begin(), stringargs.end(), std::ostream_iterator(t)); t<<"\\n\"\n"; return t.str(); } //------------------------------------------------------------------------------ std::string shape::genShape(char const * name) const { std::stringstream sh; sh<<"static char const * "<0 ? faceuvs[idx+j]+1 : vert, normal = (int)facenormals.size()>0 ? facenormals[idx+j]+1 : vert; sh << vert << "/" << uv << "/" << normal << " "; } sh << "\\n\"\n"; idx+=nvertsPerFace[i]; } for (int i=0; i<(int)tags.size(); ++i) sh << tags[i]->genTag(); return sh.str(); } //------------------------------------------------------------------------------ std::string shape::genObj(char const * name) const { std::stringstream sh; sh<<"# This file uses centimeters as units for non-parametric coordinates.\n\n"; for (int i=0; i<(int)verts.size(); i+=3) sh << "v " << verts[i] << " " << verts[i+1] << " " << verts[i+2] <<"\n"; for (int i=0; i<(int)uvs.size(); i+=2) sh << "vt " << uvs[i] << " " << uvs[i+1] << "\n"; for (int i=0; i<(int)normals.size(); i+=3) sh << "vn " << normals[i] << " " << normals[i+1] << " " << normals[i+2] <<"\n"; for (int i=0, idx=0; i<(int)nvertsPerFace.size();++i) { sh << "f "; for (int j=0; j0 ? faceuvs[idx+j]+1 : vert, normal = (int)facenormals.size()>0 ? facenormals[idx+j]+1 : vert; sh << vert << "/" << uv << "/" << normal << " "; } sh << "\n"; idx+=nvertsPerFace[i]; } for (int i=0; i<(int)tags.size(); ++i) sh << tags[i]->genTag(); return sh.str(); } //------------------------------------------------------------------------------ std::string shape::genRIB() const { std::stringstream rib; rib << "HierarchicalSubdivisionMesh \"catmull-clark\" "; rib << "["; std::copy(nvertsPerFace.begin(), nvertsPerFace.end(), std::ostream_iterator(rib)); rib << "] "; rib << "["; std::copy(faceverts.begin(), faceverts.end(), std::ostream_iterator(rib)); rib << "] "; std::stringstream names, nargs, intargs, floatargs, strargs; for (int i=0; i<(int)tags.size();) { tag * t = tags[i]; names << t->name; nargs << t->intargs.size() << " " << t->floatargs.size() << " " << t->stringargs.size(); std::copy(t->intargs.begin(), t->intargs.end(), std::ostream_iterator(intargs)); std::copy(t->floatargs.begin(), t->floatargs.end(), std::ostream_iterator(floatargs)); std::copy(t->stringargs.begin(), t->stringargs.end(), std::ostream_iterator(strargs)); if (++i<(int)tags.size()) { names << " "; nargs << " "; intargs << " "; floatargs << " "; strargs << " "; } } rib << "["<(rib)); rib << "] "; return rib.str(); } //------------------------------------------------------------------------------ shape * shape::parseShape(char const * shapestr, int axis ) { shape * s = new shape; char * str=const_cast(shapestr), line[256]; bool done = false; while( not done ) { done = sgets(line, sizeof(line), &str)==0; char* end = &line[strlen(line)-1]; if (*end == '\n') *end = '\0'; // strip trailing nl float x, y, z, u, v; switch (line[0]) { case 'v': switch (line[1]) { case ' ': if(sscanf(line, "v %f %f %f", &x, &y, &z) == 3) { s->verts.push_back(x); switch( axis ) { case 0 : s->verts.push_back(-z); s->verts.push_back(y); break; case 1 : s->verts.push_back(y); s->verts.push_back(z); break; } } break; case 't': if(sscanf(line, "vt %f %f", &u, &v) == 2) { s->uvs.push_back(u); s->uvs.push_back(v); } break; case 'n' : if(sscanf(line, "vn %f %f %f", &x, &y, &z) == 3) { s->normals.push_back(x); s->normals.push_back(y); s->normals.push_back(z); } break; // skip normals for now } break; case 'f': if(line[1] == ' ') { int vi, ti, ni; const char* cp = &line[2]; while (*cp == ' ') cp++; int nverts = 0, nitems=0; while( (nitems=sscanf(cp, "%d/%d/%d", &vi, &ti, &ni))>0) { nverts++; s->faceverts.push_back(vi-1); if(nitems >= 1) s->faceuvs.push_back(ti-1); if(nitems >= 2) s->facenormals.push_back(ni-1); while (*cp && *cp != ' ') cp++; while (*cp == ' ') cp++; } s->nvertsPerFace.push_back(nverts); } break; case 't' : if(line[1] == ' ') { shape::tag * t = tag::parseTag( line ); if (t) s->tags.push_back(t); } break; } } return s; } //------------------------------------------------------------------------------ template void applyTags( OpenSubdiv::HbrMesh * mesh, shape const * sh ) { for (int i=0; i<(int)sh->tags.size(); ++i) { shape::tag * t = sh->tags[i]; if (t->name=="crease") { for (int j=0; j<(int)t->intargs.size()-1; j += 2) { OpenSubdiv::HbrVertex * v = mesh->GetVertex( t->intargs[j] ), * w = mesh->GetVertex( t->intargs[j+1] ); OpenSubdiv::HbrHalfedge * e = 0; if( v && w ) { if((e = v->GetEdge(w)) == 0) e = w->GetEdge(v); if(e) { int nfloat = (int) t->floatargs.size(); e->SetSharpness( std::max(0.0f, ((nfloat > 1) ? t->floatargs[j] : t->floatargs[0])) ); } else printf("cannot find edge for crease tag (%d,%d)\n", t->intargs[j], t->intargs[j+1] ); } } } else if (t->name=="corner") { for (int j=0; j<(int)t->intargs.size(); ++j) { OpenSubdiv::HbrVertex * v = mesh->GetVertex( t->intargs[j] ); if(v) { int nfloat = (int) t->floatargs.size(); v->SetSharpness( std::max(0.0f, ((nfloat > 1) ? t->floatargs[j] : t->floatargs[0])) ); } else printf("cannot find vertex for corner tag (%d)\n", t->intargs[j] ); } } else if (t->name=="hole") { for (int j=0; j<(int)t->intargs.size(); ++j) { OpenSubdiv::HbrFace * f = mesh->GetFace( t->intargs[j] ); if(f) { f->SetHole(); } else printf("cannot find face for hole tag (%d)\n", t->intargs[j] ); } } else if (t->name=="interpolateboundary") { if ((int)t->intargs.size()!=1) { printf("expecting 1 integer for \"interpolateboundary\" tag n. %d\n", i); continue; } switch( t->intargs[0] ) { case 0 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryNone); break; case 1 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeAndCorner); break; case 2 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeOnly); break; default: printf("unknown interpolate boundary : %d\n", t->intargs[0] ); break; } } else if (t->name=="facevaryinginterpolateboundary") { if ((int)t->intargs.size()!=1) { printf("expecting 1 integer for \"facevaryinginterpolateboundary\" tag n. %d\n", i); continue; } switch( t->intargs[0] ) { case 0 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryNone); break; case 1 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeAndCorner); break; case 2 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeOnly); break; case 3 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh::k_InterpolateBoundaryAlwaysSharp); break; default: printf("unknown facevarying interpolate boundary : %d\n", t->intargs[0] ); break; } } else if (t->name=="facevaryingpropagatecorners") { if ((int)t->intargs.size()==1) mesh->SetFVarPropagateCorners( t->intargs[0] != 0 ); else printf( "expecting single int argument for \"facevaryingpropagatecorners\"\n" ); } else if (t->name=="smoothtriangles") { OpenSubdiv::HbrCatmarkSubdivision * scheme = dynamic_cast *>( mesh->GetSubdivision() ); if (not scheme) { printf("the \"smoothtriangles\" tag can only be applied to Catmark meshes\n"); continue; } if ((int)t->intargs.size()==0) { printf("the \"smoothtriangles\" tag expects an int argument\n"); continue; } if( t->intargs[0]==1 ) scheme->SetTriangleSubdivisionMethod( OpenSubdiv::HbrCatmarkSubdivision::k_Old); else if( t->intargs[0]==2 ) scheme->SetTriangleSubdivisionMethod( OpenSubdiv::HbrCatmarkSubdivision::k_New); else printf("the \"smoothtriangles\" tag only accepts 1 or 2 as value (%d)\n", t->intargs[0]); } else if (t->name=="creasemethod") { OpenSubdiv::HbrSubdivision * scheme = mesh->GetSubdivision(); assert(scheme); if ((int)t->stringargs.size()==0) { printf("the \"creasemethod\" tag expects a string argument\n"); continue; } if( t->stringargs[0]=="normal" ) scheme->SetCreaseSubdivisionMethod( OpenSubdiv::HbrSubdivision::k_CreaseNormal); else if( t->stringargs[0]=="chaikin" ) scheme->SetCreaseSubdivisionMethod( OpenSubdiv::HbrSubdivision::k_CreaseChaikin); else printf("the \"creasemethod\" tag only accepts \"normal\" or \"chaikin\" as value (%s)\n", t->stringargs[0].c_str()); } else if (t->name=="vertexedit" or t->name=="edgeedit") { int nops = 0; int floatstride = 0; int maxfloatwidth = 0; std::vector::Operation > ops; std::vector opnames; std::vector varnames; std::vector::Operation > opmodifiers; std::vector floatwidths; std::vector isP; std::vector vvindex; for (int j=0; j<(int)t->stringargs.size(); j+=3) { const std::string & opname = t->stringargs[j+2]; const std::string & opmodifiername = t->stringargs[j]; const std::string & varname = t->stringargs[j+1]; typename OpenSubdiv::HbrHierarchicalEdit::Operation opmodifier = OpenSubdiv::HbrVertexEdit::Set; if (opmodifiername == "set") { opmodifier = OpenSubdiv::HbrHierarchicalEdit::Set; } else if (opmodifiername == "add") { opmodifier = OpenSubdiv::HbrHierarchicalEdit::Add; } else if (opmodifiername == "subtract") { opmodifier = OpenSubdiv::HbrHierarchicalEdit::Subtract; } else { printf("invalid modifier %s\n", opmodifiername.c_str()); continue; } if ((t->name=="vertexedit" && opname=="value") || opname=="sharpness") { nops++; // only varname="P" is supported here for now. if (varname != "P") continue; vvindex.push_back(0); isP.push_back(true); opnames.push_back(opname); opmodifiers.push_back(opmodifier); varnames.push_back(varname); if (opname=="sharpness") { floatwidths.push_back(1); floatstride += 1; } else { // assuming width of P == 3. should be replaced with 'P 0 3' like declaration int numElements = 3; maxfloatwidth = std::max(maxfloatwidth, numElements); floatwidths.push_back(numElements); floatstride += numElements; } } else { printf("%s tag specifies invalid operation '%s %s' on Subdivmesh\n", t->name.c_str(), opmodifiername.c_str(), opname.c_str()); } } float *xformed = (float*)alloca(maxfloatwidth * sizeof(float)); int floatoffset = 0; for(int j=0; jintargs.size();) { int pathlength = t->intargs[k]; int faceid = t->intargs[k+1]; int vertexid = t->intargs[k+pathlength]; int nsubfaces = pathlength - 2; int *subfaces = &t->intargs[k+2]; OpenSubdiv::HbrFace * f = mesh->GetFace(faceid); if (!f) { printf("Invalid face %d specified for %s tag on SubdivisionMesh.\n", faceid, t->name.c_str()); goto nexttag; } // Found the face. Do some preliminary error checking to make sure the path is // correct. First value in path depends on the number of vertices of the face // which we have in hand if (nsubfaces && (subfaces[0] < 0 || subfaces[0] >= f->GetNumVertices()) ) { printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); goto nexttag; } // All subsequent values must be less than 4 (FIXME or 3 in the loop case?) for (int l=1; l 3) { printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); goto nexttag; } } if (vertexid < 0 || vertexid > 3) { printf("Invalid path component (vertexid) %d in %s tag on SubdivisionMesh.\n", vertexid, t->name.c_str()); goto nexttag; } // Transform all the float values associated with the tag if needed if(opnames[j] != "sharpness") { for(int l=0; lfloatargs[l + floatidx]; } // Edits of facevarying data are a different hierarchical edit type altogether OpenSubdiv::HbrVertexEdit * edit = new OpenSubdiv::HbrVertexEdit(faceid, nsubfaces, subfaces, vertexid, vvindex[j], floatwidths[j], isP[j], opmodifiers[j], xformed); mesh->AddHierarchicalEdit(edit); } else { if (t->name == "vertexedit") { OpenSubdiv::HbrCornerEdit * edit = new OpenSubdiv::HbrCornerEdit(faceid, nsubfaces, subfaces, vertexid, opmodifiers[j], t->floatargs[floatidx]); mesh->AddHierarchicalEdit(edit); } else { OpenSubdiv::HbrCreaseEdit * edit = new OpenSubdiv::HbrCreaseEdit(faceid, nsubfaces, subfaces, vertexid, opmodifiers[j], t->floatargs[floatidx]); mesh->AddHierarchicalEdit(edit); } } // Advance to next path k += pathlength + 1; // Advance to start of float data floatidx += floatstride; } // End of integer processing loop // Next subop floatoffset += floatwidths[j]; } // End of subop processing loop } else if (t->name=="faceedit") { int nint = (int)t->intargs.size(); for (int k=0; kintargs[k]; if (k+pathlength>=nint) { printf("Invalid path length for %s tag on SubdivisionMesh", t->name.c_str()); goto nexttag; } int faceid = t->intargs[k+1]; int nsubfaces = pathlength - 1; int *subfaces = &t->intargs[k+2]; OpenSubdiv::HbrFace * f = mesh->GetFace(faceid); if (!f) { printf("Invalid face %d specified for %s tag on SubdivisionMesh.\n", faceid, t->name.c_str()); goto nexttag; } // Found the face. Do some preliminary error checking to make sure the path is // correct. First value in path depends on the number of vertices of the face // which we have in hand if (nsubfaces && (subfaces[0] < 0 || subfaces[0] >= f->GetNumVertices()) ) { printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); goto nexttag; } // All subsequent values must be less than 4 (FIXME or 3 in the loop case?) for (int l=1; l 3) { printf("Invalid path component %d in %s tag on SubdivisionMesh.\n", subfaces[0], t->name.c_str()); goto nexttag; } } // Now loop over string ops int nstring = (int)t->stringargs.size(); for (int l = 0; l < nstring; ) { if ( t->stringargs[l] == "hole" ) { // Construct the edit OpenSubdiv::HbrHoleEdit * edit = new OpenSubdiv::HbrHoleEdit(faceid, nsubfaces, subfaces); mesh->AddHierarchicalEdit(edit); ++l; } else if ( t->stringargs[l] == "attributes" ) { // see NgpSubdivMesh.cpp:4341 printf("\"attributes\" face tag not supported yet.\n"); goto nexttag; } else if ( t->stringargs[l] == "set" || t->stringargs[l] == "add" ) { // see NgpSubdivMesh.cpp:4341 printf("\"set\" and \"add\" face tag not supported yet.\n"); goto nexttag; } else { printf("Faceedit tag specifies invalid operation '%s' on Subdivmesh.\n", t->stringargs[l].c_str()); goto nexttag; } } // Advance to next path k += pathlength + 1; } // end face path loop } else { printf("Unknown tag : \"%s\" - skipping\n", t->name.c_str()); } nexttag: ; } } //------------------------------------------------------------------------------ template std::string hbrToObj( OpenSubdiv::HbrMesh * mesh ) { std::stringstream sh; sh<<"# This file uses centimeters as units for non-parametric coordinates.\n\n"; int nv = mesh->GetNumVertices(); for (int i=0; iGetVertex(i)->GetData().GetPos(); sh << "v " << pos[0] << " " << pos[1] << " " << pos[2] <<"\n"; } int nf = mesh->GetNumFaces(); for (int i=0; i * f = mesh->GetFace(i); for (int j=0; jGetNumVertices(); ++j) { int vert = f->GetVertex(j)->GetID()+1; sh << vert << "/" << vert << "/" << vert << " "; } sh << "\n"; } sh << "\n"; return sh.str(); } //------------------------------------------------------------------------------ template OpenSubdiv::HbrMesh * createMesh( Scheme scheme=kCatmark, int fvarwidth=0) { OpenSubdiv::HbrMesh * mesh = 0; static OpenSubdiv::HbrBilinearSubdivision _bilinear; static OpenSubdiv::HbrLoopSubdivision _loop; static OpenSubdiv::HbrCatmarkSubdivision _catmark; static int indices[2] = { 0, 1 }, widths[2] = { 1, 1 }; int const fvarcount = fvarwidth > 0 ? 2 : 0, * fvarindices = fvarwidth > 0 ? indices : NULL, * fvarwidths = fvarwidth > 0 ? widths : NULL; switch (scheme) { case kBilinear : mesh = new OpenSubdiv::HbrMesh( &_bilinear, fvarcount, fvarindices, fvarwidths, fvarwidth ); break; case kLoop : mesh = new OpenSubdiv::HbrMesh( &_loop, fvarcount, fvarindices, fvarwidths, fvarwidth ); break; case kCatmark : mesh = new OpenSubdiv::HbrMesh( &_catmark, fvarcount, fvarindices, fvarwidths, fvarwidth ); break; } return mesh; } //------------------------------------------------------------------------------ template void createVertices( shape const * sh, OpenSubdiv::HbrMesh * mesh, std::vector * verts ) { T v; for(int i=0;igetNverts(); i++ ) { v.SetPosition( sh->verts[i*3], sh->verts[i*3+1], sh->verts[i*3+2] ); mesh->NewVertex( i, v ); } } //------------------------------------------------------------------------------ template void createVertices( shape const * sh, OpenSubdiv::HbrMesh * mesh, std::vector & verts ) { T v; for(int i=0;igetNverts(); i++ ) mesh->NewVertex( i, v ); } //------------------------------------------------------------------------------ template void copyVertexPositions( shape const * sh, OpenSubdiv::HbrMesh * mesh, std::vector & verts ) { int nverts = mesh->GetNumVertices(); verts.resize( nverts * 3 ); std::copy(sh->verts.begin(), sh->verts.end(), verts.begin()); // Sometimes Hbr dupes some vertices during Mesh::Finish() and our example // code uses those vertices to draw coarse control cages and such std::vector > const splits = mesh->GetSplitVertices(); for (int i=0; i<(int)splits.size(); ++i) { memcpy(&verts[splits[i].first*3], &sh->verts[splits[i].second*3], 3*sizeof(float)); } } //------------------------------------------------------------------------------ template void createTopology( shape const * sh, OpenSubdiv::HbrMesh * mesh, Scheme scheme) { const int * fv=&(sh->faceverts[0]); for(int f=0, ptxidx=0;fgetNfaces(); f++ ) { int nv = sh->nvertsPerFace[f]; if ((scheme==kLoop) and (nv!=3)) { printf("Trying to create a Loop subd with non-triangle face\n"); exit(1); } bool valid = true; for(int j=0;j * origin = mesh->GetVertex( fv[j] ); OpenSubdiv::HbrVertex * destination = mesh->GetVertex( fv[(j+1)%nv] ); OpenSubdiv::HbrHalfedge * opposite = destination->GetEdge(origin); if(origin==NULL || destination==NULL) { printf(" An edge was specified that connected a nonexistent vertex\n"); valid=false; break; } if(origin == destination) { printf(" An edge was specified that connected a vertex to itself\n"); valid=false; break; } if(opposite && opposite->GetOpposite() ) { printf(" A non-manifold edge incident to more than 2 faces was found\n"); valid=false; break; } if(origin->GetEdge(destination)) { printf(" An edge connecting two vertices was specified more than once." " It's likely that an incident face was flipped\n"); valid=false; break; } } if (valid) { OpenSubdiv::HbrFace * face = mesh->NewFace(nv, (int *)fv, 0); face->SetPtexIndex(ptxidx); if ( (scheme==kCatmark or scheme==kBilinear) and nv != 4 ) { ptxidx+=nv; } else { ptxidx++; } } fv+=nv; } mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryEdgeOnly); mesh->GetSubdivision()->SetCreaseSubdivisionMethod( OpenSubdiv::HbrSubdivision::k_CreaseNormal); if (OpenSubdiv::HbrCatmarkSubdivision * scheme = dynamic_cast *>(mesh->GetSubdivision())) { scheme->SetTriangleSubdivisionMethod( OpenSubdiv::HbrCatmarkSubdivision::k_Normal); } applyTags( mesh, sh ); mesh->Finish(); // check for disconnected vertices if (mesh->GetNumDisconnectedVertices()) { printf("The specified subdivmesh contains disconnected surface components.\n"); } } //------------------------------------------------------------------------------ template void createFaceVaryingUV( shape const * sh, OpenSubdiv::HbrMesh * mesh) { if (not sh->hasUV()) return; for (int i=0, idx=0; igetNfaces(); ++i ) { OpenSubdiv::HbrFace * f = mesh->GetFace(i); int nv = sh->nvertsPerFace[i]; OpenSubdiv::HbrHalfedge * e = f->GetFirstEdge(); for (int j=0; jGetNext()) { OpenSubdiv::HbrFVarData & fvt = e->GetOrgVertex()->GetFVarData(f); float const * fvdata = &sh->uvs[ sh->faceuvs[idx++]*2 ]; if (not fvt.IsInitialized()) { fvt.SetAllData(2, fvdata); } else if (not fvt.CompareAll(2, fvdata)) { OpenSubdiv::HbrFVarData & nfvt = e->GetOrgVertex()->NewFVarData(f); nfvt.SetAllData(2, fvdata); } } } } //------------------------------------------------------------------------------ template OpenSubdiv::HbrMesh * simpleHbr(char const * shapestr, Scheme scheme, std::vector * verts=0, bool fvar=false) { shape * sh = shape::parseShape( shapestr ); int fvarwidth = fvar and sh->hasUV() ? 2 : 0; OpenSubdiv::HbrMesh * mesh = createMesh(scheme, fvarwidth); createVertices(sh, mesh, verts); createTopology(sh, mesh, scheme); if (fvar) createFaceVaryingUV(sh, mesh); if (verts) copyVertexPositions(sh,mesh,*verts); delete sh; return mesh; } //------------------------------------------------------------------------------ template OpenSubdiv::HbrMesh * simpleHbr(char const * shapestr, Scheme scheme, std::vector & verts, bool fvar=false) { shape * sh = shape::parseShape( shapestr ); int fvarwidth = fvar and sh->hasUV() ? 2 : 0; OpenSubdiv::HbrMesh * mesh = createMesh(scheme, fvarwidth); createVertices(sh, mesh, verts); createTopology(sh, mesh, scheme); if (fvar) createFaceVaryingUV(sh, mesh); copyVertexPositions(sh,mesh,verts); delete sh; return mesh; } #endif /* SHAPE_UTILS_H */