Modify Far remapping of singular vertices to point to their source vertex.

- Add a vector of index pairs to HbrMesh to track the index of a split
  vertex and its origin vertex
- Correct the Far remap tables in FarSubdivisionTablesFactory to point split
  vertices to their origin instead of themselves
- Fix regression/common/shape_utils.h to use the new HbrMesh::GetSplitVertices()
  method.
- Fix the osdPolySmooth example to use the new HbrMesh::GetSplitVertices()
  method.
- Add a paragraph to the documentation

fixes #241
This commit is contained in:
manuelk 2013-11-21 16:05:31 -08:00
parent 25f6565238
commit c3cb17fa99
7 changed files with 127 additions and 137 deletions

View File

@ -1,26 +1,26 @@
..
..
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.
Using Hbr
---------
@ -40,7 +40,7 @@ expected to provide a vertex class that implements the requisite interpolation
functionality.
Here is an example of a simple vertex class that accounts for 3D position, but
does not support arbitrary variables or varying interpolation.
does not support arbitrary variables or varying interpolation.
.. code:: c++
@ -54,10 +54,10 @@ does not support arbitrary variables or varying interpolation.
~Vertex( ) { }
void AddWithWeight(const Vertex& src, float weight, void * =0 ) {
_pos[0]+=weight*src._pos[0];
_pos[1]+=weight*src._pos[1];
_pos[2]+=weight*src._pos[2];
void AddWithWeight(const Vertex& src, float weight, void * =0 ) {
_pos[0]+=weight*src._pos[0];
_pos[1]+=weight*src._pos[1];
_pos[2]+=weight*src._pos[2];
}
void AddVaryingWithWeight(const Vertex& , float, void * =0 ) { }
@ -90,7 +90,7 @@ does not support arbitrary variables or varying interpolation.
void ApplyMovingVertexEdit(const OpenSubdiv::HbrMovingVertexEdit<Vertex> &) { }
// custom functions & data not required by Hbr -------------------------
Vertex( float x, float y, float z ) { _pos[0]=x; _pos[1]=y; _pos[2]=z; }
const float * GetPos() const { return _pos; }
@ -131,25 +131,25 @@ Creating a Mesh
===============
The following tutorial walks through the steps of instantiating a simple **Hbr**
mesh.
mesh.
The code found in regression/common/shape_utils.h can also be used as an example.
While this implementation covers many of **Hbr**'s features, it does not provide
coverage for the complete Renderman specification though.
coverage for the complete Renderman specification though.
----
Instantiating an HbrMesh
************************
First we need to instantiate a mesh object.
First we need to instantiate a mesh object.
**Hbr** supports 3 subdivision schemes:
* Catmull-Clark (catmark)
* Loop
* Bilinear
The scheme is selected by passing an specialized instance of *HbrSubdivision<T>*,
The scheme is selected by passing an specialized instance of *HbrSubdivision<T>*,
*HbrCatmarkSubdivision<T>* in this case. The scheme can be shared across multiple
mesh objects, so we only need a single instance.
@ -175,7 +175,7 @@ the following design pattern:
Vertex vtx;
for(int i=0;i<numVerts; i++ ) {
Vertex * v = mesh->NewVertex( i, vtx);
// v->SetPosition();
}
@ -194,29 +194,29 @@ faces with *HbrMesh::NewFace()*. Assuming we had an *obj* style reader, we need
to know the number of vertices in the face and the indices of these vertices.
.. code:: c++
for (int f=0; f<numFaces; ++f) {
int nverts = obj->GetNumVertices(f);
const int * faceverts = obj->GetFaceVerts(f);
mesh->NewFace(nv, fv, 0);
}
However, currently **Hbr** is not able to handle `non-manifold <subdivision_surfaces.html#manifold-geometry>`__
geometry. In order to avoid tripping asserts or causing memory violations, let's
rewrite the previous loop with some some prototype code to check the validity of
However, currently **Hbr** is not able to handle `non-manifold <subdivision_surfaces.html#manifold-geometry>`__
geometry. In order to avoid tripping asserts or causing memory violations, let's
rewrite the previous loop with some some prototype code to check the validity of
the topology.
.. code:: c++
for (int f=0; f<numFaces; ++f) {
int nv = obj->GetNumVertices(f);
const int * fv = obj->GetFaceVerts(f);
// triangles only for Loop subdivision !
if ((scheme==kLoop) and (nv!=3)) {
printf("Trying to create a Loop subd with non-triangle face\n");
@ -250,7 +250,7 @@ the topology.
continue;
}
}
mesh->NewFace(nv, fv, 0);
}
@ -259,7 +259,7 @@ the topology.
Wrapping Things Up
******************
Once we have vertices and faces set in our mesh, we still need to wrap things up
Once we have vertices and faces set in our mesh, we still need to wrap things up
by calling *HbrMesh::Finish()*:
.. code:: c++
@ -276,7 +276,7 @@ remaining to validate the mesh:
if (mesh->GetNumDisconnectedVertices()) {
printf("The specified subdivmesh contains disconnected surface components.\n");
// abort or iterate over the mesh to remove the offending vertices
}
@ -305,7 +305,7 @@ The rule-set can be selected using the following accessors:
**Warning**
The boundary interpolation rules **must** be set before the call to
The boundary interpolation rules **must** be set before the call to
*HbrMesh::Finish()*, which sets the sharpness values to boundary edges
and vertices based on these rules.
@ -326,12 +326,12 @@ Vertex Creases
Given an index, vertices are very easy to access in the mesh.
.. code:: c++
int idx; // vertex index
float sharp; // the edge sharpness
OpenSubdiv::HbrVertex<Vertex> * v = mesh->GetVertex( idx );
if(v) {
v->SetSharpness( std::max(0.0f, sharp) );
} else
@ -342,7 +342,7 @@ Given an index, vertices are very easy to access in the mesh.
Edge Creases
************
Usually, edge creases are described with a vertex indices pair. Here is some
Usually, edge creases are described with a vertex indices pair. Here is some
sample code to locate the matching half-edge and set a crease sharpness.
.. code:: c++
@ -352,9 +352,9 @@ sample code to locate the matching half-edge and set a crease sharpness.
OpenSubdiv::HbrVertex<Vertex> * v = mesh->GetVertex( v0 ),
* w = mesh->GetVertex( v1 );
OpenSubdiv::HbrHalfedge<Vertex> * e = 0;
if( v && w ) {
if((e = v->GetEdge(w)) == 0)
@ -390,8 +390,8 @@ Holes
**Note**
The hole tag is hierarchical : sub-faces can also be marked as holes.
The hole tag is hierarchical : sub-faces can also be marked as holes.
See: `Hierarchical Edits`_
----
@ -435,31 +435,31 @@ to `this example <subdivision_surfaces.html#hierarchical-edits-paths>`__.
.. code:: c++
// path = 655, 2, 3, 0
int faceid = 655,
int faceid = 655,
nsubfaces = 2,
subfaces[2] = { 2, 3 },
vertexid = 0;
int offset = 0, // offset to the vertex or varying data
numElems = 3; // number of elements to apply the modifier to (x,y,z = 3)
bool isP = false; // shortcut to identify modifications to the vertex position "P"
OpenSubdiv::HbrHierarchicalEdit<Vertex>::Operation op =
OpenSubdiv::HbrHierarchicalEdit<Vertex>::Operation op =
OpenSubdiv::HbrHierarchicalEdit<T>::Set;
float values[3] = { 1.0f, 0.5f, 0.0f }; // edit values
OpenSubdiv::HbrVertexEdit<T> * edit =
OpenSubdiv::HbrVertexEdit<T> * edit =
new OpenSubdiv::HbrVertexEdit<T>(faceid,
nsubfaces,
subfaces,
vertexid,
subfaces,
vertexid,
offset,
floatwidth,
isP,
op,
values);
isP,
op,
values);
----
@ -503,30 +503,30 @@ Here is some sample code:
.. code:: c++
for (int i=0, idx=0; i<numFaces; ++i ) {
OpenSubdiv::HbrFace<Vertex> * f = mesh->GetFace(i);
int nv = f->GetNumVertices(); // note: this is not the fastest way
OpenSubdiv::HbrHalfedge<Vertex> * e = f->GetFirstEdge();
for (int j=0; j<nv; ++j, e=e->GetNext()) {
OpenSubdiv::HbrFVarData<Vertex> & fvt = e->GetOrgVertex()->GetFVarData(f);
float const * fvdata = GetFaceVaryingData( i, j );
if (not fvt.IsInitialized()) {
// if no fvar daa exists yet on the vertex
fvt.SetAllData(2, fvdata);
} else if (not fvt.CompareAll(2, fvdata)) {
// if there already is fvar data and there is a boundary add the new data
OpenSubdiv::HbrFVarData<T> & nfvt = e->GetOrgVertex()->NewFVarData(f);
nfvt.SetAllData(2, fvdata);
}
}
}
@ -544,9 +544,9 @@ From a face, passing a vertex index:
.. code:: c++
// OpenSubdiv::HbrFace<Vertex> * f
OpenSubdiv::HbrFVarData const &fv = f.GetFVarData(vindex);
const float * data = fv.GetData()
@ -576,7 +576,7 @@ vertices (use HbrVertex::GetValence() for proper valence counts)
.. code:: c++
//OpenSubdiv::HbrVertex<Vertex> * v;
class MyOperator : public OpenSubdiv::HbrVertexOperator<Vertex> {
public:
@ -588,8 +588,37 @@ vertices (use HbrVertex::GetValence() for proper valence counts)
++count;
}
};
MyOperator op;
v->ApplyOperatorSurroundingVertices( op );
----
Managing Singular Vertices
==========================
Certain topological configurations would force vertices to share multiple
half-edge cycles. Because Hbr is a half-edge representation, these "singular"
vertices have to be duplicated as part of the HbrMesh::Finish() phase of the
instantiation.
These duplicated vertices can cause problems for client-code that tries to
populate buffers of vertex or varying data. The following sample code shows
how to match the vertex data to singular vertex splits:
.. code:: c++
// Populating an OsdCpuVertexBuffer with vertex data (positions,...)
float const * vtxData = inMeshFn.getRawPoints(&returnStatus);
OpenSubdiv::OsdCpuVertexBuffer *vertexBuffer =
OpenSubdiv::OsdCpuVertexBuffer::Create(numVertexElements, numFarVerts);
vertexBuffer->UpdateData(vtxData, 0, numVertices );
// Duplicate the vertex data into the split singular vertices
std::vector<std::pair<int, int> > const splits = hbrMesh->GetSplitVertices();
for (int i=0; i<(int)splits.size(); ++i) {
vertexBuffer->UpdateData(vtxData+splits[i].second*numVertexElements, splits[i].first, 1);
}

View File

@ -785,8 +785,6 @@ MStatus OsdPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {
// NOTE: This HAS to be called after all HBR parameters are set
hbrMesh->Finish();
int ncoarseverts = hbrMesh->GetNumVertices();
// Create a FarMesh from the HBR mesh and pass into
// It will be owned by the OsdMesh and deleted in the ~OsdMesh()
FMeshFactory meshFactory(hbrMesh, subdivisionLevel, false);
@ -817,31 +815,9 @@ MStatus OsdPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {
// Hbr dupes singular vertices during Mesh::Finish() - we need
// to duplicate their positions in the vertex buffer.
if (ncoarseverts > numVertices) {
MIntArray polyverts;
for (int i=numVertices; i<ncoarseverts; ++i) {
HVertex const * v = hbrMesh->GetVertex(i);
HFace const * f = v->GetIncidentEdge()->GetFace();
int vidx = -1;
for (int j=0; j<f->GetNumVertices(); ++j) {
if (f->GetVertex(j)==v) {
vidx = j;
break;
}
}
assert(vidx>-1);
inMeshFn.getPolygonVertices(f->GetID(), polyverts);
int vert = polyverts[vidx];
vertexBuffer->UpdateData(&vertex3fArray[0]+vert*numVertexElements, i, 1);
}
std::vector<std::pair<int, int> > const splits = hbrMesh->GetSplitVertices();
for (int i=0; i<(int)splits.size(); ++i) {
vertexBuffer->UpdateData(&vertex3fArray[0]+splits[i].second*numVertexElements, splits[i].first, 1);
}
// == Delete HBR

View File

@ -285,8 +285,6 @@ FarMeshFactory<T,U>::refine( HbrMesh<T> * mesh, int maxlevel ) {
// faces that have already been refined.
firstface = nfaces;
}
mesh->SetSubdivisionMethod(HbrMesh<T>::k_SubdivisionMethodUniform);
}
// Scan the faces of a mesh and compute the max level of subdivision required
@ -578,9 +576,6 @@ FarMeshFactory<T,U>::refineAdaptive( HbrMesh<T> * mesh, int maxIsolate ) {
}
}
}
mesh->SetSubdivisionMethod(HbrMesh<T>::k_SubdivisionMethodFeatureAdaptive);
return maxlevel-1;
}

View File

@ -236,7 +236,11 @@ FarSubdivisionTablesFactory<T,U>::FarSubdivisionTablesFactory( HbrMesh<T> const
for (size_t i=0; i<_vertVertsList[l].size(); ++i)
remapTable[ _vertVertsList[l][i]->GetID() ]=_vertVertIdx[l]+(int)i;
// Remap singular vertices to their origin vertices
std::vector<std::pair<int, int> > const & singulars = mesh->GetSplitVertices();
for (int i=0; i<(int)singulars.size(); ++i) {
remapTable[singulars[i].first]=singulars[i].second;
}
}

View File

@ -427,16 +427,19 @@ private:
#ifdef HBR_ADAPTIVE
public:
enum SubdivisionMethod {
k_SubdivisionMethodNone,
k_SubdivisionMethodUniform,
k_SubdivisionMethodFeatureAdaptive
};
void SetSubdivisionMethod(SubdivisionMethod method) { _subdivisionMethod=method; }
SubdivisionMethod GetSubdivisionMethod() const { return _subdivisionMethod; }
std::vector<std::pair<int, int> > const & GetSplitVertices() const {
return m_splitVertices;
}
protected:
friend class HbrVertex<T>;
void addSplitVertex(int splitIdx, int orgIdx) {
m_splitVertices.push_back(std::pair<int,int>(splitIdx, orgIdx));
}
private:
SubdivisionMethod _subdivisionMethod;
std::vector<std::pair<int, int> > m_splitVertices;
#endif
};

View File

@ -412,11 +412,10 @@ private:
#ifdef HBR_ADAPTIVE
public:
struct adaptiveFlags {
unsigned subdivisions:4;
unsigned isTagged:1;
unsigned wasTagged:1;
adaptiveFlags() : subdivisions(0), isTagged(0), wasTagged(0) { }
adaptiveFlags() : isTagged(0), wasTagged(0) { }
};
adaptiveFlags _adaptiveFlags;
@ -1524,6 +1523,9 @@ HbrVertex<T>::splitSingular() {
}
}
w->Finish();
#ifdef HBR_ADAPTIVE
mesh->addSplitVertex(w->GetID(), this->GetID());
#endif
}
e = incidentEdges[0];

View File

@ -25,6 +25,10 @@
#ifndef SHAPE_UTILS_H
#define SHAPE_UTILS_H
#ifndef HBR_ADAPTIVE
#define HBR_ADAPTIVE
#endif
#include <hbr/mesh.h>
#include <hbr/bilinear.h>
#include <hbr/loop.h>
@ -432,7 +436,7 @@ void applyTags( OpenSubdiv::HbrMesh<T> * mesh, shape const * sh ) {
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() );
@ -775,32 +779,9 @@ copyVertexPositions( shape const * sh, OpenSubdiv::HbrMesh<T> * mesh, std::vecto
std::copy(sh->verts.begin(), sh->verts.end(), verts.begin());
// Sometimes Hbr dupes some vertices during Mesh::Finish()
if (nverts > sh->getNverts()) {
for (int i=sh->getNverts(); i<nverts; ++i) {
OpenSubdiv::HbrVertex<T> * v = mesh->GetVertex(i);
OpenSubdiv::HbrFace<T> * f = v->GetIncidentEdge()->GetFace();
int vidx = -1;
for (int j=0; j<f->GetNumVertices(); ++j)
if (f->GetVertex(j)==v) {
vidx = j;
break;
}
assert(vidx>-1);
const int * shfaces = &sh->faceverts[0];
for (int j=0; j<f->GetID(); ++j)
shfaces += sh->nvertsPerFace[j];
int shvert = shfaces[vidx];
verts[i*3+0] = sh->verts[shvert*3+0];
verts[i*3+1] = sh->verts[shvert*3+1];
verts[i*3+2] = sh->verts[shvert*3+2];
}
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));
}
}
@ -872,10 +853,10 @@ createTopology( shape const * sh, OpenSubdiv::HbrMesh<T> * mesh, Scheme scheme)
mesh->GetSubdivision()->SetCreaseSubdivisionMethod(
OpenSubdiv::HbrSubdivision<T>::k_CreaseNormal);
if (OpenSubdiv::HbrCatmarkSubdivision<T> * scheme =
dynamic_cast<OpenSubdiv::HbrCatmarkSubdivision<T> *>(mesh->GetSubdivision())) {
scheme->SetTriangleSubdivisionMethod(
OpenSubdiv::HbrCatmarkSubdivision<T>::k_Normal);
}