OpenSubdiv/regression/common/shape_utils.h

462 lines
17 KiB
C++

//
// Copyright (C) Pixar. All rights reserved.
//
// This license governs use of the accompanying software. If you
// use the software, you accept this license. If you do not accept
// the license, do not use the software.
//
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and
// "distribution" have the same meaning here as under U.S.
// copyright law. A "contribution" is the original software, or
// any additions or changes to the software.
// A "contributor" is any person or entity that distributes its
// contribution under this license.
// "Licensed patents" are a contributor's patent claims that read
// directly on its contribution.
//
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free copyright license to reproduce its contribution,
// prepare derivative works of its contribution, and distribute
// its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free license under its licensed patents to make, have
// made, use, sell, offer for sale, import, and/or otherwise
// dispose of its contribution in the software or derivative works
// of the contribution in the software.
//
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you
// rights to use any contributor's name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over
// patents that you claim are infringed by the software, your
// patent license from such contributor to the software ends
// automatically.
// (C) If you distribute any portion of the software, you must
// retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source
// code form, you may do so only under this license by including a
// complete copy of this license with your distribution. If you
// distribute any portion of the software in compiled or object
// code form, you may only do so under a license that complies
// with this license.
// (E) The software is licensed "as-is." You bear the risk of
// using it. The contributors give no express warranties,
// guarantees or conditions. You may have additional consumer
// rights under your local laws which this license cannot change.
// To the extent permitted under your local laws, the contributors
// exclude the implied warranties of merchantability, fitness for
// a particular purpose and non-infringement.
//
#ifndef SHAPE_UTILS_H
#define SHAPE_UTILS_H
#include <stdio.h>
#include <string.h>
#include <list>
#include <string>
#include <vector>
//------------------------------------------------------------------------------
static char const * sgets( char * s, int size, char ** stream ) {
for (int i=0; i<size; ++i) {
if ( (*stream)[i]=='\n' or (*stream)[i]=='\0') {
memcpy(s, *stream, i);
s[i]='\0';
if ((*stream)[i]=='\0')
return 0;
else {
(*stream) += i+1;
return s;
}
}
}
return 0;
}
//------------------------------------------------------------------------------
struct shape {
struct tag {
static tag * parseTag( char const * stream );
std::string name;
std::vector<int> intargs;
std::vector<float> floatargs;
std::vector<std::string> stringargs;
};
static shape * parseShape(char const * shapestr, int axis=1);
~shape();
int getNverts() const { return (int)verts.size()/3; }
int getNfaces() const { return (int)nvertsPerFace.size(); }
std::vector<float> verts;
std::vector<float> uvs;
std::vector<int> nvertsPerFace;
std::vector<int> faceverts;
std::vector<int> faceuvs;
std::vector<tag *> tags;
};
//------------------------------------------------------------------------------
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[15];
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<int> intargs;
for (int i=0; i<nints; ++i) {
int val;
while (*cp == ' ') cp++;
if (sscanf(cp, "%d", &val)!=1) return t;
intargs.push_back(val);
while (*cp && *cp != ' ') cp++;
}
std::vector<float> floatargs;
for (int i=0; i<nfloats; ++i) {
float val;
while (*cp == ' ') cp++;
if (sscanf(cp, "%f", &val)!=1) return t;
floatargs.push_back(val);
while (*cp && *cp != ' ') cp++;
}
std::vector<std::string> stringargs;
for (int i=0; i<nstrings; ++i) {
char * val;
while (*cp == ' ') cp++;
if (sscanf(cp, "%s", &val)!=1) return t;
stringargs.push_back(val);
while (*cp && *cp != ' ') cp++;
}
t = new shape::tag;
t->name = name;
t->intargs = intargs;
t->floatargs = floatargs;
t->stringargs = stringargs;
return t;
}
//------------------------------------------------------------------------------
shape * shape::parseShape(char const * shapestr, int axis ) {
shape * s = new shape;
char * str=const_cast<char *>(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' : 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);
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 <class T>
void applyTags( 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) {
HbrVertex<T> * v = mesh->GetVertex( t->intargs[j] ),
* w = mesh->GetVertex( t->intargs[j+1] );
HbrHalfedge<T> * e = 0;
if( v && w ) {
if( !(e = v->GetEdge(w) ) )
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) {
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) {
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(HbrMesh<T>::k_InterpolateBoundaryNone); break;
case 1 : mesh->SetInterpolateBoundaryMethod(HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner); break;
case 2 : mesh->SetInterpolateBoundaryMethod(HbrMesh<T>::k_InterpolateBoundaryEdgeOnly); break;
default: printf("unknown interpolation 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=="creasemethod") {
HbrCatmarkSubdivision<T> * scheme =
dynamic_cast<HbrCatmarkSubdivision<T> *>( mesh->GetSubdivision() );
if (not scheme) {
printf("the \"creasemethod\" tag can only be applied to Catmark meshes\n");
continue;
}
if ((int)t->stringargs.size()==0) {
printf("the \"creasemethod\" tag expects a string argument\n");
continue;
}
if( t->stringargs[0]=="normal" )
scheme->SetTriangleSubdivisionMethod(HbrCatmarkSubdivision<T>::k_Old);
else if( t->stringargs[0]=="chaikin" )
scheme->SetTriangleSubdivisionMethod(HbrCatmarkSubdivision<T>::k_New);
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") {
printf("hierarchical edits not supported (yet)\n");
} else {
printf("Unknown tag : \"%s\" - skipping\n", t->name.c_str());
}
}
}
enum Scheme {
kBilinear,
kCatmark,
kLoop
};
//------------------------------------------------------------------------------
template <class T> HbrMesh<T> *
createMesh( Scheme scheme=kCatmark) {
HbrMesh<T> * mesh = 0;
static HbrBilinearSubdivision<T> _bilinear;
static HbrLoopSubdivision<T> _loop;
static HbrCatmarkSubdivision<T> _catmark;
switch (scheme) {
case kBilinear : mesh = new HbrMesh<T>( &_bilinear ); break;
case kLoop : mesh = new HbrMesh<T>( &_loop ); break;
case kCatmark : mesh = new HbrMesh<T>( &_catmark ); break;
}
return mesh;
}
//------------------------------------------------------------------------------
template <class T> void
createVertices( shape const * sh, HbrMesh<T> * mesh ) {
T v;
for(int i=0;i<sh->getNverts(); 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, HbrMesh<T> * mesh, std::vector<float> & verts ) {
int nverts = sh->getNverts();
verts.resize(nverts);
T v;
for(int i=0;i<nverts; i++ ) {
mesh->NewVertex( i, v );
verts[i*3 ]=sh->verts[i*3 ];
verts[i*3+1]=sh->verts[i*3+1];
verts[i*3+2]=sh->verts[i*3+2];
}
}
//------------------------------------------------------------------------------
template <class T> void
createTopology( shape const * sh, HbrMesh<T> * mesh, Scheme scheme) {
const int * fv=&(sh->faceverts[0]);
for(int f=0, ptxidx=0;f<sh->getNfaces(); f++ ) {
int nv = sh->nvertsPerFace[f];
if ((scheme==kLoop) and (nv!=3)) {
printf("Trying to create a Loop surbd with non-triangle face\n");
exit(1);
}
for(int j=0;j<nv;j++) {
HbrVertex<T> * origin = mesh->GetVertex( fv[j] );
HbrVertex<T> * destination = mesh->GetVertex( fv[ (j+1)%nv] );
HbrHalfedge<T> * opposite = destination->GetEdge(origin);
if(origin==NULL || destination==NULL) {
printf(" An edge was specified that connected a nonexistent vertex\n");
exit(1);
}
if(origin == destination) {
printf(" An edge was specified that connected a vertex to itself\n");
exit(1);
}
if(opposite && opposite->GetOpposite() ) {
printf(" A non-manifold edge incident to more than 2 faces was found\n");
exit(1);
}
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");
exit(1);
}
}
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;
}
applyTags<T>( mesh, sh );
mesh->Finish();
}
//------------------------------------------------------------------------------
template <class T> HbrMesh<T> *
simpleHbr( char const * shapestr, Scheme scheme=kCatmark) {
shape * sh = shape::parseShape( shapestr );
HbrMesh<T> * mesh = createMesh<T>(scheme);
createVertices<T>(sh, mesh);
createTopology<T>(sh, mesh, scheme);
delete sh;
return mesh;
}
//------------------------------------------------------------------------------
template <class T> HbrMesh<T> *
simpleHbr( char const * shapestr, std::vector<float> & verts, Scheme scheme=kCatmark) {
shape * sh = shape::parseShape( shapestr );
HbrMesh<T> * mesh = createMesh<T>(scheme);
createVertices<T>(sh, mesh, verts);
createTopology<T>(sh, mesh, scheme);
delete sh;
return mesh;
}
#endif /* SHAPE_UTILS_H */