Merge pull request #502 from barfowl/patch_eval

Refactor public patch evaluation methods exposed by Far::PatchTables
This commit is contained in:
David G Yu 2015-05-20 19:39:32 -07:00
commit c82b3d551d
10 changed files with 150 additions and 421 deletions

View File

@ -446,7 +446,8 @@ createFVarPatches(OpenSubdiv::Far::TopologyRefiner const & refiner,
for (int i=0; i<tessFactor; ++i) {
for (int j=0; j<tessFactor; ++j, ++vert) {
handle.patchIndex = patch;
patchTables.EvaluateFaceVarying(channel, handle, uvs[i], uvs[j], fvarBuffer, *vert);
// To be replaced with EvaluateBasis() for the appropriate channel:
//patchTables.EvaluateFaceVarying(channel, handle, uvs[i], uvs[j], fvarBuffer, *vert);
}
}
}

View File

@ -30,7 +30,7 @@ set(SOURCE_FILES
endCapGregoryBasisPatchFactory.cpp
endCapLegacyGregoryPatchFactory.cpp
gregoryBasis.cpp
interpolate.cpp
patchBasis.cpp
patchDescriptor.cpp
patchMap.cpp
patchTables.cpp
@ -51,7 +51,7 @@ set(PUBLIC_HEADER_FILES
endCapGregoryBasisPatchFactory.h
endCapLegacyGregoryPatchFactory.h
gregoryBasis.h
interpolate.h
patchBasis.h
patchDescriptor.h
patchParam.h
patchMap.h

View File

@ -1,245 +0,0 @@
//
// 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 OPENSUBDIV3_FAR_INTERPOLATE_H
#define OPENSUBDIV3_FAR_INTERPOLATE_H
#include "../version.h"
#include "../far/patchParam.h"
#include "../far/stencilTables.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
// XXXX
//
// Note 1 : interpolation functions will eventually need to be augmented to
// handle second order derivatives
//
// Note 2 : evaluation of derivatives need to be ptional (passing NULL to
// dQ1 and dQ2 to the weights functions will do this)
void GetBilinearWeights(PatchParam::BitField bits,
float s, float t, float point[4], float deriv1[4], float deriv2[4]);
void GetBezierWeights(PatchParam::BitField bits,
float s, float t, float point[16], float deriv1[16], float deriv2[16]);
void GetBSplineWeights(PatchParam::BitField bits,
float s, float t, float point[16], float deriv1[16], float deriv2[16]);
void GetGregoryWeights(PatchParam::BitField bits,
float s, float t, float point[20], float deriv1[20], float deriv2[20]);
/// \brief Interpolate the (s,t) parametric location of a bilinear (quad)
/// patch
///
/// @param cvs Array of 16 control vertex indices
///
/// @param Q Array of 16 bicubic weights for the control vertices
///
/// @param Qd1 Array of 16 bicubic 's' tangent weights for the control
/// vertices
///
/// @param Qd2 Array of 16 bicubic 't' tangent weights for the control
/// vertices
///
/// @param src Source primvar buffer (control vertices data)
///
/// @param dst Destination primvar buffer (limit surface data)
///
template <class T, class U>
inline void
InterpolateBilinearPatch(Index const * cvs,
float const * Q, float const *Qd1, float const *Qd2,
T const & src, U & dst) {
//
// v0 -- v1
// |.....|
// |.....|
// v3 -- v2
//
for (int k=0; k<4; ++k) {
dst.AddWithWeight(src[cvs[k]], Q[k], Qd1[k], Qd2[k]);
}
}
/// \brief Interpolate the (s,t) parametric location of a regular bicubic
/// patch
///
/// @param cvs Array of 16 control vertex indices
///
/// @param Q Array of 16 bicubic weights for the control vertices
///
/// @param Qd1 Array of 16 bicubic 's' tangent weights for the control
/// vertices
///
/// @param Qd2 Array of 16 bicubic 't' tangent weights for the control
/// vertices
///
/// @param src Source primvar buffer (control vertices data)
///
/// @param dst Destination primvar buffer (limit surface data)
///
template <class T, class U>
inline void
InterpolateRegularPatch(Index const * cvs,
float const * Q, float const *Qd1, float const *Qd2,
T const & src, U & dst) {
//
// v0 -- v1 -- v2 -- v3
// |.....|.....|.....|
// |.....|.....|.....|
// v4 -- v5 -- v6 -- v7
// |.....|.....|.....|
// |.....|.....|.....|
// v8 -- v9 -- v10-- v11
// |.....|.....|.....|
// |.....|.....|.....|
// v12-- v13-- v14-- v15
//
for (int k=0; k<16; ++k) {
dst.AddWithWeight(src[cvs[k]], Q[k], Qd1[k], Qd2[k]);
}
}
/// \brief Interpolate the (s,t) parametric location of a Gregory bicubic
/// patch
///
/// @param cvs Array of 20 control vertex indices
///
/// @param s Patch coordinate (in coarse face normalized space)
///
/// @param t Patch coordinate (in coarse face normalized space)
///
/// @param Q Array of 9 bicubic weights for the control vertices
///
/// @param Qd1 Array of 9 bicubic 's' tangent weights for the control
/// vertices
///
/// @param Qd2 Array of 9 bicubic 't' tangent weights for the control
/// vertices
///
/// @param src Source primvar buffer (control vertices data)
///
/// @param dst Destination primvar buffer (limit surface data)
///
template <class T, class U>
inline void
InterpolateGregoryPatch(Index const *cvs, float s, float t,
float const * Q, float const *Qd1, float const *Qd2,
T const & src, U & dst) {
float ss = 1-s,
tt = 1-t;
// remark #1572: floating-point equality and inequality comparisons are unreliable
#ifdef __INTEL_COMPILER
#pragma warning disable 1572
#endif
float d11 = s+t; if(s+t==0.0f) d11 = 1.0f;
float d12 = ss+t; if(ss+t==0.0f) d12 = 1.0f;
float d21 = s+tt; if(s+tt==0.0f) d21 = 1.0f;
float d22 = ss+tt; if(ss+tt==0.0f) d22 = 1.0f;
#ifdef __INTEL_COMPILER
#pragma warning enable 1572
#endif
float weights[4][2] = { { s/d11, t/d11 },
{ ss/d12, t/d12 },
{ s/d21, tt/d21 },
{ ss/d22, tt/d22 } };
//
// P3 e3- e2+ P2
// 15------17-------11--------10
// | | | |
// | | | |
// | | f3- | f2+ |
// | 19 13 |
// e3+ 16-----18 14-----12 e2-
// | f3+ f2- |
// | |
// | |
// | f0- f1+ |
// e0- 2------4 8------6 e1+
// | 3 9 |
// | | f0+ | f1- |
// | | | |
// | | | |
// O--------1--------7--------5
// P0 e0+ e1- P1
//
// gregory-to-bezier map
static int const permute[16] =
{ 0, 1, 7, 5, 2, -1, -2, 6, 16, -3, -4, 12, 15, 17, 11, 10 };
for (int k = 0; k < 16; ++k) {
int index = permute[k];
if (index >=0) {
dst.AddWithWeight(src[cvs[index]], Q[k], Qd1[k], Qd2[k]);
} else if (index == -1) {
// 3, 4
float w0 = weights[0][0];
float w1 = weights[0][1];
dst.AddWithWeight(src[cvs[3]], w0*Q[k], w0*Qd1[k], w0*Qd2[k]);
dst.AddWithWeight(src[cvs[4]], w1*Q[k], w1*Qd1[k], w1*Qd2[k]);
} else if (index == -2) {
// 8, 9
float w0 = weights[1][0];
float w1 = weights[1][1];
dst.AddWithWeight(src[cvs[9]], w0*Q[k], w0*Qd1[k], w0*Qd2[k]);
dst.AddWithWeight(src[cvs[8]], w1*Q[k], w1*Qd1[k], w1*Qd2[k]);
} else if (index == -3) {
// 18, 19
float w0 = weights[2][0];
float w1 = weights[2][1];
dst.AddWithWeight(src[cvs[19]], w0*Q[k], w0*Qd1[k], w0*Qd2[k]);
dst.AddWithWeight(src[cvs[18]], w1*Q[k], w1*Qd1[k], w1*Qd2[k]);
} else if (index == -4) {
// 13, 14
float w0 = weights[3][0];
float w1 = weights[3][1];
dst.AddWithWeight(src[cvs[13]], w0*Q[k], w0*Qd1[k], w0*Qd2[k]);
dst.AddWithWeight(src[cvs[14]], w1*Q[k], w1*Qd1[k], w1*Qd2[k]);
}
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_INTERPOLATE_H */

View File

@ -22,7 +22,7 @@
// language governing permissions and limitations under the Apache License.
//
#include "../far/interpolate.h"
#include "../far/patchBasis.h"
#include <cassert>
#include <cstring>
@ -31,6 +31,7 @@ namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
enum SplineBasis {
BASIS_BILINEAR,
@ -424,6 +425,7 @@ void GetGregoryWeights(PatchParam::BitField bits,
}
}
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION

View File

@ -0,0 +1,69 @@
//
// 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 OPENSUBDIV3_FAR_PATCH_BASIS_H
#define OPENSUBDIV3_FAR_PATCH_BASIS_H
#include "../version.h"
#include "../far/patchParam.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
//
// XXXX barfowl: These functions are being kept in place while more complete
// underlying support for all patch types is being worked out. That support
// will include a larger set of patch types (eventually including triangular
// patches for Loop) and arbitrary differentiation of all (to support second
// derivatives and other needs).
//
// So this interface will be changing in future.
//
void GetBilinearWeights(PatchParam::BitField bits,
float s, float t, float wP[4], float wDs[4], float wDt[4]);
void GetBezierWeights(PatchParam::BitField bits,
float s, float t, float wP[16], float wDs[16], float wDt[16]);
void GetBSplineWeights(PatchParam::BitField bits,
float s, float t, float wP[16], float wDs[16], float wDt[16]);
void GetGregoryWeights(PatchParam::BitField bits,
float s, float t, float wP[20], float wDs[20], float wDt[20]);
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_BASIS_H */

View File

@ -23,6 +23,7 @@
//
#include "../far/patchTables.h"
#include "../far/patchBasis.h"
#include <cstring>
#include <cstdio>
@ -507,6 +508,27 @@ PatchTables::print() const {
}
}
//
// Evaluate basis functions for position and first derivatives at (s,t):
//
void
PatchTables::EvaluateBasis(PatchHandle const & handle, float s, float t,
float wP[], float wDs[], float wDt[]) const {
PatchDescriptor::Type patchType = GetPatchArrayDescriptor(handle.arrayIndex).GetType();
PatchParam::BitField const & patchBits = _paramTable[handle.patchIndex].bitField;
if (patchType == PatchDescriptor::REGULAR) {
internal::GetBSplineWeights(patchBits, s, t, wP, wDs, wDt);
} else if (patchType == PatchDescriptor::GREGORY_BASIS) {
internal::GetGregoryWeights(patchBits, s, t, wP, wDs, wDt);
} else if (patchType == PatchDescriptor::QUADS) {
internal::GetBilinearWeights(patchBits, s, t, wP, wDs, wDt);
} else {
assert(0);
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION

View File

@ -27,8 +27,9 @@
#include "../version.h"
#include "../far/interpolate.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../far/stencilTables.h"
#include "../sdc/options.h"
@ -277,13 +278,11 @@ public:
public:
//@{
/// @name Interpolation methods
//
/// \brief Interpolate the (s,t) parametric location of a *bilinear* patch
/// @name Evaluation methods
///
/// \note This method can only be used on uniform PatchTables of quads (see
/// IsFeatureAdaptive() method)
/// \brief Evaluate basis functions for position and first derivatives at a
/// given (s,t) parametric location of a patch.
///
/// @param handle A patch handle indentifying the sub-patch containing the
/// (s,t) location
@ -292,51 +291,14 @@ public:
///
/// @param t Patch coordinate (in coarse face normalized space)
///
/// @param src Source primvar buffer (control vertices data)
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param dst Destination primvar buffer (limit surface data)
/// @param wDs Weights (evaluated basis functions) for derivative wrt s
///
template <class T, class U> void EvaluateBilinear(PatchHandle const & handle,
float s, float t, T const & src, U & dst) const;
/// \brief Interpolate the (s,t) parametric location of a *bicubic* patch
/// @param wDt Weights (evaluated basis functions) for derivative wrt t
///
/// \note This method can only be used on feature adaptive PatchTables (ie.
/// IsFeatureAdaptive() is false)
///
/// @param handle A patch handle indentifying the sub-patch containing the
/// (s,t) location
///
/// @param s Patch coordinate (in coarse face normalized space)
///
/// @param t Patch coordinate (in coarse face normalized space)
///
/// @param src Source primvar buffer (control vertices data)
///
/// @param dst Destination primvar buffer (limit surface data)
///
template <class T, class U> void Evaluate(PatchHandle const & handle,
float s, float t, T const & src, U & dst) const;
/// \brief Interpolate the (s,t) parametric location of a *bicubic*
/// face-varying patch
///
/// @param channel The face-varying primvar channel
///
/// @param handle A patch handle indentifying the sub-patch containing the
/// (s,t) location
///
/// @param s Patch coordinate (in coarse face normalized space)
///
/// @param t Patch coordinate (in coarse face normalized space)
///
/// @param src Source primvar buffer (control vertices data)
///
/// @param dst Destination primvar buffer (limit surface data)
///
template <class T, class U> void EvaluateFaceVarying(int channel,
PatchHandle const & handle, float s, float t, T const & src, U & dst) const;
void EvaluateBasis(PatchHandle const & handle, float s, float t,
float wP[], float wDs[], float wDt[]) const;
//@}
@ -442,117 +404,6 @@ private:
std::vector<float> _sharpnessValues; // Sharpness values.
};
// XXXX manuelk evaluation should have the following interface :
// - EvaluateVertex<>()
// - EvaluateVarying<>()
// - EvaluateFaceVarying<>()
// this refactor is pending the move of fvar channels as a private data
// structure inside PatchTables, along with the addition of accessors that
// use PatchHandle and work that hides the indexing of the patches inside
// the tables
// Interpolates primvar limit at the given parametric location on a patch
template <class T, class U>
inline void
PatchTables::Evaluate(PatchHandle const & handle, float s, float t,
T const & src, U & dst) const {
assert(IsFeatureAdaptive());
PatchParam::BitField const & bits = _paramTable[handle.patchIndex].bitField;
PatchDescriptor::Type ptype =
GetPatchArrayDescriptor(handle.arrayIndex).GetType();
dst.Clear();
float Q[16], Qd1[16], Qd2[16];
if (ptype==PatchDescriptor::REGULAR) {
ConstIndexArray cvs = GetPatchVertices(handle);
GetBSplineWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateRegularPatch(cvs.begin(), Q, Qd1, Qd2, src, dst);
} else if (ptype==PatchDescriptor::GREGORY_BASIS) {
ConstIndexArray cvs = GetPatchVertices(handle);
GetBezierWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateGregoryPatch(cvs.begin(), s, t, Q, Qd1, Qd2, src, dst);
} else if (ptype==PatchDescriptor::QUADS) {
ConstIndexArray cvs = GetPatchVertices(handle);
GetBilinearWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateBilinearPatch(cvs.begin(), Q, Qd1, Qd2, src, dst);
} else {
assert(0);
}
}
// Interpolates the limit position of a parametric location on a face-varying
// patch
// XXXX manuelk this method is very similar to the vertex Evaluate<>() method
// -> we should eventually merge them
template <class T, class U>
inline void
PatchTables::EvaluateFaceVarying(int channel, PatchHandle const & handle,
float s, float t, T const & src, U & dst) const {
ConstIndexArray cvs = GetFVarPatchValues(channel, handle);
PatchDescriptor::Type type = GetFVarPatchType(channel, handle);
PatchParam::BitField bits;
bits.Clear();
float Q[16], Qd1[16], Qd2[16];
switch (type) {
case PatchDescriptor::QUADS:
GetBilinearWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateBilinearPatch(cvs.begin(), Q, Qd1, Qd2, src, dst);
break;
case PatchDescriptor::TRIANGLES:
assert("not implemented yet");
case PatchDescriptor::REGULAR:
GetBSplineWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateRegularPatch(cvs.begin(), Q, Qd1, Qd2, src, dst);
break;
default:
assert(0);
// XXXX manuelk - how do we handle end-patches ?
// - is there a bicubic patch that we could use to reduce
// isolation of bilinear boundaries with smooth a interior ?
}
}
// Interpolates primvar at the given parametric location on a bilinear patch
template <class T, class U>
inline void
PatchTables::EvaluateBilinear(PatchHandle const & handle, float s, float t,
T const & src, U & dst) const {
ConstIndexArray cvs = GetPatchVertices(handle);
assert(cvs.size()==4);
PatchParam::BitField const & bits =
_paramTable[handle.patchIndex].bitField;
dst.Clear();
float Q[4], Qd1[4], Qd2[4];
GetBilinearWeights(bits, s, t, Q, Qd1, Qd2);
InterpolateBilinearPatch(cvs.begin(), Q, Qd1, Qd2, src, dst);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION

View File

@ -495,6 +495,8 @@ LimitStencilTablesFactory::Create(TopologyRefiner const & refiner,
// XXXX (manuelk) we can make uniform (bilinear) stencils faster with a
// dedicated code path that does not use PatchTables or the PatchMap
float wP[20], wDs[20], wDt[20];
for (int i=0, currentStencil=0; i<(int)locationArrays.size(); ++i) {
LocationArray const & array = locationArrays[i];
@ -506,16 +508,22 @@ LimitStencilTablesFactory::Create(TopologyRefiner const & refiner,
float s = array.s[j],
t = array.t[j];
PatchMap::Handle const * handle =
patchmap.FindPatch(array.ptexIdx, s, t);
PatchMap::Handle const * handle = patchmap.FindPatch(array.ptexIdx, s, t);
if (handle) {
ConstIndexArray cvs = patchTables->GetPatchVertices(*handle);
patchTables->EvaluateBasis(*handle, s, t, wP, wDs, wDt);
StencilTables const & src = *cvstencils;
ProtoLimitStencil dst = alloc[currentStencil];
if (uniform) {
patchtables->EvaluateBilinear(*handle, s, t, *cvstencils, dst);
} else {
patchtables->Evaluate(*handle, s, t, *cvstencils, dst);
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k]);
}
++numLimitStencils;
}
}

View File

@ -130,11 +130,20 @@ CpuEvaluator::EvalPatches(const float *src,
BufferAdapter<const float> srcT(src, srcDesc.length, srcDesc.stride);
BufferAdapter<float> dstT(dst, dstDesc.length, dstDesc.stride);
float wP[20], wDs[20], wDt[20];
for (size_t i = 0; i < patchCoords.size(); ++i) {
PatchCoord const &coords = patchCoords[i];
patchTable->Evaluate(coords.handle, coords.s, coords.t,
srcT, dstT);
patchTable->EvaluateBasis(coords.handle, coords.s, coords.t, wP, wDs, wDt);
Far::ConstIndexArray cvs = patchTable->GetPatchVertices(coords.handle);
dstT.Clear();
for (int j = 0; j < cvs.size(); ++j) {
dstT.AddWithWeight(srcT[cvs[j]], wP[j], wDs[j], wDt[j]);
}
++count;
++dstT;
}

View File

@ -33,7 +33,7 @@
// adaptive isolation of topological features. This process converts the
// input polygonal control cage into a collection of bi-cubic patches.
//
// We can then evaluate the patches are random parametric locations and
// We can then evaluate the patches at random parametric locations and
// obtain analytical positions and tangents on the limit surface.
//
// The results are dumped into a MEL script that draws 'streak' particle
@ -102,7 +102,8 @@ struct Vertex {
};
//------------------------------------------------------------------------------
// Limit frame container implementation.
// Limit frame container implementation -- this interface is not strictly
// required but follows a similar pattern to Vertex.
//
struct LimitFrame {
@ -174,6 +175,8 @@ int main(int, char **) {
srand( static_cast<int>(2147483647) );
float pWeights[20], dsWeights[20], dtWeights[20];
for (int face=0, count=0; face<nfaces; ++face) {
for (int sample=0; sample<nsamples; ++sample, ++count) {
@ -186,8 +189,17 @@ int main(int, char **) {
patchmap.FindPatch(face, s, t);
assert(handle);
// Evaluate the limit frame
patchTables->Evaluate(*handle, s, t, &verts[0], samples[count]);
// Evaluate the patch weights, identify the CVs and compute the limit frame:
patchTables->EvaluateBasis(*handle, s, t, pWeights, dsWeights, dtWeights);
Far::ConstIndexArray cvs = patchTables->GetPatchVertices(*handle);
LimitFrame & dst = samples[count];
dst.Clear();
for (int cv=0; cv < cvs.size(); ++cv) {
dst.AddWithWeight(verts[cvs[cv]], pWeights[cv], dsWeights[cv], dtWeights[cv]);
}
}
}