mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-27 22:10:06 +00:00
89dcea57c2
All examples, regression tests and tutorials directly looked into opensubdiv source directory to grab the header files. This is somewhat convenient during development but they can mistakenly access private header files. With this change, when OPENSUBDIV_INCLUDE_DIR is given to cmake, it will be used as an include search path to build examples etc. Otherwise it follows the same behavior as before. Also replaces include references to the files in regression dir to be relative, and cleanups some copy-paste patterns.
695 lines
27 KiB
C++
695 lines
27 KiB
C++
//
|
|
// 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 HBR_UTILS_H
|
|
#define HBR_UTILS_H
|
|
|
|
#ifndef HBR_ADAPTIVE
|
|
#define HBR_ADAPTIVE
|
|
#endif
|
|
|
|
#include <hbr/mesh.h>
|
|
#include <hbr/bilinear.h>
|
|
#include <hbr/loop.h>
|
|
#include <hbr/catmark.h>
|
|
#include <hbr/vertexEdit.h>
|
|
#include <hbr/cornerEdit.h>
|
|
#include <hbr/holeEdit.h>
|
|
|
|
#include "shape_utils.h"
|
|
|
|
#include <sstream>
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T>
|
|
void applyTags( OpenSubdiv::HbrMesh<T> * 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<T> * v = mesh->GetVertex( t->intargs[j] ),
|
|
* w = mesh->GetVertex( t->intargs[j+1] );
|
|
OpenSubdiv::HbrHalfedge<T> * 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<T> * 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<T> * 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<T>::k_InterpolateBoundaryNone); break;
|
|
case 1 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner); break;
|
|
case 2 : mesh->SetInterpolateBoundaryMethod(OpenSubdiv::HbrMesh<T>::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<T>::k_InterpolateBoundaryNone); break;
|
|
case 1 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner); break;
|
|
case 2 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh<T>::k_InterpolateBoundaryEdgeOnly); break;
|
|
case 3 : mesh->SetFVarInterpolateBoundaryMethod(OpenSubdiv::HbrMesh<T>::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<T> * scheme =
|
|
dynamic_cast<OpenSubdiv::HbrCatmarkSubdivision<T> *>( 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<T>::k_Old);
|
|
else if( t->intargs[0]==2 )
|
|
scheme->SetTriangleSubdivisionMethod(
|
|
OpenSubdiv::HbrCatmarkSubdivision<T>::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<T> * 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<T>::k_CreaseNormal);
|
|
else if( t->stringargs[0]=="chaikin" )
|
|
scheme->SetCreaseSubdivisionMethod(
|
|
OpenSubdiv::HbrSubdivision<T>::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<typename OpenSubdiv::HbrHierarchicalEdit<T>::Operation > ops;
|
|
std::vector<std::string> opnames;
|
|
std::vector<std::string> varnames;
|
|
std::vector<typename OpenSubdiv::HbrHierarchicalEdit<T>::Operation > opmodifiers;
|
|
std::vector<int> floatwidths;
|
|
std::vector<bool> isP;
|
|
std::vector<int> 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<T>::Operation opmodifier = OpenSubdiv::HbrVertexEdit<T>::Set;
|
|
if (opmodifiername == "set") {
|
|
opmodifier = OpenSubdiv::HbrHierarchicalEdit<T>::Set;
|
|
} else if (opmodifiername == "add") {
|
|
opmodifier = OpenSubdiv::HbrHierarchicalEdit<T>::Add;
|
|
} else if (opmodifiername == "subtract") {
|
|
opmodifier = OpenSubdiv::HbrHierarchicalEdit<T>::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; j<nops; ++j) {
|
|
int floatidx = floatoffset;
|
|
for (int k=0; k < (int)t->intargs.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<T> * 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<nsubfaces; ++l) {
|
|
if (subfaces[l] < 0 || subfaces[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; l<floatwidths[j]; ++l) {
|
|
xformed[l] = t->floatargs[l + floatidx];
|
|
}
|
|
|
|
// Edits of facevarying data are a different hierarchical edit type altogether
|
|
OpenSubdiv::HbrVertexEdit<T> * edit = new OpenSubdiv::HbrVertexEdit<T>(faceid, nsubfaces, subfaces,
|
|
vertexid, vvindex[j], floatwidths[j],
|
|
isP[j], opmodifiers[j], xformed);
|
|
mesh->AddHierarchicalEdit(edit);
|
|
} else {
|
|
if (t->name == "vertexedit") {
|
|
OpenSubdiv::HbrCornerEdit<T> * edit = new OpenSubdiv::HbrCornerEdit<T>(faceid, nsubfaces, subfaces,
|
|
vertexid, opmodifiers[j], t->floatargs[floatidx]);
|
|
mesh->AddHierarchicalEdit(edit);
|
|
} else {
|
|
OpenSubdiv::HbrCreaseEdit<T> * edit = new OpenSubdiv::HbrCreaseEdit<T>(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; k<nint; ) {
|
|
int pathlength = t->intargs[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<T> * 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<nsubfaces; ++l) {
|
|
if (subfaces[l] < 0 || subfaces[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<T> * edit = new OpenSubdiv::HbrHoleEdit<T>(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 <class T> std::string
|
|
hbrToObj( OpenSubdiv::HbrMesh<T> * 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; i<nv; ++i) {
|
|
const float * pos = mesh->GetVertex(i)->GetData().GetPos();
|
|
sh << "v " << pos[0] << " " << pos[1] << " " << pos[2] <<"\n";
|
|
}
|
|
|
|
int nf = mesh->GetNumFaces();
|
|
for (int i=0; i<nf; ++i) {
|
|
|
|
sh << "f ";
|
|
|
|
OpenSubdiv::HbrFace<T> * f = mesh->GetFace(i);
|
|
|
|
for (int j=0; j<f->GetNumVertices(); ++j) {
|
|
int vert = f->GetVertex(j)->GetID()+1;
|
|
sh << vert << "/" << vert << "/" << vert << " ";
|
|
}
|
|
sh << "\n";
|
|
}
|
|
|
|
sh << "\n";
|
|
|
|
return sh.str();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> OpenSubdiv::HbrMesh<T> *
|
|
createMesh( Scheme scheme=kCatmark, int fvarwidth=0) {
|
|
|
|
OpenSubdiv::HbrMesh<T> * mesh = 0;
|
|
|
|
static OpenSubdiv::HbrBilinearSubdivision<T> _bilinear;
|
|
static OpenSubdiv::HbrLoopSubdivision<T> _loop;
|
|
static OpenSubdiv::HbrCatmarkSubdivision<T> _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<T>( &_bilinear,
|
|
fvarcount,
|
|
fvarindices,
|
|
fvarwidths,
|
|
fvarwidth ); break;
|
|
|
|
case kLoop : mesh = new OpenSubdiv::HbrMesh<T>( &_loop,
|
|
fvarcount,
|
|
fvarindices,
|
|
fvarwidths,
|
|
fvarwidth ); break;
|
|
|
|
case kCatmark : mesh = new OpenSubdiv::HbrMesh<T>( &_catmark,
|
|
fvarcount,
|
|
fvarindices,
|
|
fvarwidths,
|
|
fvarwidth ); break;
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> void
|
|
createVerticesWithPositions(Shape const * sh, OpenSubdiv::HbrMesh<T> * mesh) {
|
|
|
|
T v;
|
|
for(int i=0;i<sh->GetNumVertices(); i++ ) {
|
|
v.SetPosition( sh->verts[i*3], sh->verts[i*3+1], sh->verts[i*3+2] );
|
|
mesh->NewVertex( i, v );
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> void
|
|
createVertices(Shape const * sh, OpenSubdiv::HbrMesh<T> * mesh) {
|
|
|
|
T v;
|
|
for(int i=0;i<sh->GetNumVertices(); i++ )
|
|
mesh->NewVertex( i, v );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> void
|
|
copyVertexPositions( Shape const * sh, OpenSubdiv::HbrMesh<T> * mesh, std::vector<float> & 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<std::pair<int, int> > 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 <class T> void
|
|
createTopology( Shape const * sh, OpenSubdiv::HbrMesh<T> * mesh, Scheme scheme) {
|
|
|
|
const int * fv=&(sh->faceverts[0]);
|
|
for(int f=0, ptxidx=0;f<sh->GetNumFaces(); 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<nv;j++) {
|
|
OpenSubdiv::HbrVertex<T> * origin = mesh->GetVertex( fv[j] );
|
|
OpenSubdiv::HbrVertex<T> * destination = mesh->GetVertex( fv[(j+1)%nv] );
|
|
OpenSubdiv::HbrHalfedge<T> * 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<T> * 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<T>::k_InterpolateBoundaryEdgeOnly);
|
|
|
|
mesh->GetSubdivision()->SetCreaseSubdivisionMethod(
|
|
OpenSubdiv::HbrSubdivision<T>::k_CreaseNormal);
|
|
|
|
if (OpenSubdiv::HbrCatmarkSubdivision<T> * hscheme =
|
|
dynamic_cast<OpenSubdiv::HbrCatmarkSubdivision<T> *>(mesh->GetSubdivision())) {
|
|
|
|
hscheme->SetTriangleSubdivisionMethod(
|
|
OpenSubdiv::HbrCatmarkSubdivision<T>::k_Normal);
|
|
}
|
|
|
|
applyTags<T>( mesh, sh );
|
|
|
|
mesh->Finish();
|
|
|
|
// check for disconnected vertices
|
|
if (mesh->GetNumDisconnectedVertices()) {
|
|
printf("The specified subdivmesh contains disconnected surface components.\n");
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> void
|
|
createFaceVaryingUV( Shape const * sh, OpenSubdiv::HbrMesh<T> * mesh) {
|
|
|
|
if (not sh->HasUV())
|
|
return;
|
|
|
|
for (int i=0, idx=0; i<sh->GetNumFaces(); ++i ) {
|
|
|
|
OpenSubdiv::HbrFace<T> * f = mesh->GetFace(i);
|
|
|
|
int nv = sh->nvertsPerFace[i];
|
|
|
|
OpenSubdiv::HbrHalfedge<T> * e = f->GetFirstEdge();
|
|
|
|
for (int j=0; j<nv; ++j, e=e->GetNext()) {
|
|
|
|
OpenSubdiv::HbrFVarData<T> & 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<T> & nfvt = e->GetOrgVertex()->NewFVarData(f);
|
|
nfvt.SetAllData(2, fvdata);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> OpenSubdiv::HbrMesh<T> *
|
|
simpleHbr(char const * Shapestr, Scheme scheme, std::vector<float> * verts=0, bool fvar=false) {
|
|
|
|
Shape * sh = Shape::parseObj( Shapestr, scheme );
|
|
|
|
int fvarwidth = fvar and sh->HasUV() ? 2 : 0;
|
|
|
|
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme, fvarwidth);
|
|
|
|
createVerticesWithPositions<T>(sh, mesh);
|
|
|
|
createTopology<T>(sh, mesh, scheme);
|
|
|
|
if (fvar)
|
|
createFaceVaryingUV<T>(sh, mesh);
|
|
|
|
if (verts)
|
|
copyVertexPositions<T>(sh,mesh,*verts);
|
|
|
|
delete sh;
|
|
|
|
return mesh;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T> OpenSubdiv::HbrMesh<T> *
|
|
simpleHbr(char const * Shapestr, Scheme scheme, std::vector<float> & verts, bool fvar=false) {
|
|
|
|
Shape * sh = Shape::parseObj( Shapestr, scheme );
|
|
|
|
int fvarwidth = fvar and sh->HasUV() ? 2 : 0;
|
|
|
|
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme, fvarwidth);
|
|
|
|
createVertices<T>(sh, mesh);
|
|
|
|
createTopology<T>(sh, mesh, scheme);
|
|
|
|
if (fvar)
|
|
createFaceVaryingUV<T>(sh, mesh);
|
|
|
|
copyVertexPositions<T>(sh,mesh,verts);
|
|
|
|
delete sh;
|
|
|
|
return mesh;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <class T>
|
|
OpenSubdiv::HbrMesh<T> *
|
|
interpolateHbrVertexData(char const * Shapestr, Scheme scheme, int maxlevel) {
|
|
|
|
// Hbr interpolation
|
|
OpenSubdiv::HbrMesh<T> *hmesh = simpleHbr<T>(Shapestr, scheme,
|
|
/* verts vector */ 0, /* fvar */ false);
|
|
assert(hmesh);
|
|
|
|
for (int level=0, firstface=0; level<maxlevel; ++level ) {
|
|
int nfaces = hmesh->GetNumFaces();
|
|
for (int i=firstface; i<nfaces; ++i) {
|
|
|
|
OpenSubdiv::HbrFace<T> * f = hmesh->GetFace(i);
|
|
assert(f->GetDepth()==level);
|
|
if (not f->IsHole()) {
|
|
f->Refine();
|
|
}
|
|
}
|
|
// Hbr allocates faces sequentially, skip faces that have already been
|
|
// refined.
|
|
firstface = nfaces;
|
|
}
|
|
|
|
return hmesh;
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Returns true if a vertex or any of its parents is on a boundary
|
|
template <class T>
|
|
bool
|
|
hbrVertexOnBoundary(const OpenSubdiv::HbrVertex<T> *v)
|
|
{
|
|
if (not v)
|
|
return false;
|
|
|
|
if (v->OnBoundary())
|
|
return true;
|
|
|
|
OpenSubdiv::HbrVertex<T> const * pv = v->GetParentVertex();
|
|
if (pv)
|
|
return hbrVertexOnBoundary(pv);
|
|
else {
|
|
OpenSubdiv::HbrHalfedge<T> const * pe = v->GetParentEdge();
|
|
if (pe) {
|
|
return hbrVertexOnBoundary(pe->GetOrgVertex()) or
|
|
hbrVertexOnBoundary(pe->GetDestVertex());
|
|
} else {
|
|
OpenSubdiv::HbrFace<T> const * pf = v->GetParentFace(), * rootf = pf;
|
|
while (pf) {
|
|
pf = pf->GetParent();
|
|
if (pf)
|
|
rootf=pf;
|
|
}
|
|
if (rootf)
|
|
for (int i=0; i<rootf->GetNumVertices(); ++i)
|
|
if (rootf->GetVertex(i)->OnBoundary())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
#endif /* HBR_UTILS_H */
|