// // Copyright 2021 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. // #include "../bfr/surface.h" #include "../bfr/surfaceData.h" #include "../bfr/pointOperations.h" #include "../bfr/patchTree.h" #include "../far/patchParam.h" #include "../far/patchDescriptor.h" #include "../far/patchBasis.h" #include #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Bfr { // // Constructor for the Surface -- defers to the constructor for its full // set of member variables, but marks the precision as double when needed // (as a specialization here): // template Surface::Surface() : _data() { // Surface<> should not be adding members outside its SurfaceData: assert(sizeof(*this) == sizeof(internal::SurfaceData)); } template <> Surface::Surface() : _data() { _data.setDouble(true); } // // Simple internal utilities: // template inline internal::IrregularPatchType const & Surface::getIrregPatch() const { return _data.getIrregPatch(); } template int Surface::GetNumPatchPoints() const { if (IsRegular()) { return GetNumControlPoints(); } else if (IsLinear()) { return 2 * GetNumControlPoints() + 1; } else { return getIrregPatch().GetNumPointsTotal(); } } template int Surface::GetControlPointIndices(Index cvs[]) const { std::memcpy(cvs, _data.getCVIndices(), _data.getNumCVs() * sizeof(Index)); return _data.getNumCVs(); } // // Methods for gathering and computing control and patch points: // template template void Surface::GatherControlPoints( REAL_MESH const meshPoints[], PointDescriptor const & meshDesc, REAL controlPoints[], PointDescriptor const & controlDesc) const { // // Assemble parameters of the point copy operation and apply: // typedef points::CopyConsecutive PointCopier; typename PointCopier::Parameters copyParams; copyParams.pointData = meshPoints; copyParams.pointSize = meshDesc.size; copyParams.pointStride = meshDesc.stride; copyParams.srcCount = GetNumControlPoints(); copyParams.srcIndices = _data.getCVIndices(); copyParams.resultData = controlPoints; copyParams.resultStride = controlDesc.stride; PointCopier::Apply(copyParams); } template void Surface::computeLinearPatchPoints(REAL pointData[], PointDescriptor const & pointDesc) const { // // The initial control points of the N-sided face will be followed // by the midpoint of the face and the midpoint of the N edges. // // Assemble parameters of the face splitting operation and apply: // int N = GetNumControlPoints(); typedef points::SplitFace PointSplitter; typename PointSplitter::Parameters splitParams; splitParams.pointData = pointData; splitParams.pointSize = pointDesc.size; splitParams.pointStride = pointDesc.stride; splitParams.srcCount = N; splitParams.resultData = pointData + pointDesc.stride * N; PointSplitter::Apply(splitParams); } template void Surface::computeIrregularPatchPoints(REAL pointData[], PointDescriptor const & pointDesc) const { // // An "irregular patch" may be represented by a regular patch in // rare cases, so be sure there are patch points to compute: // internal::IrregularPatchType const & irregPatch = getIrregPatch(); int numControlPoints = GetNumControlPoints(); int numPatchPoints = irregPatch.GetNumPointsTotal(); if (numPatchPoints == numControlPoints) return; // // Assemble parameters of the point combination operation and apply: // typedef points::CombineConsecutive PointCombiner; typename PointCombiner::Parameters combParams; combParams.pointData = pointData; combParams.pointSize = pointDesc.size; combParams.pointStride = pointDesc.stride; combParams.srcCount = numControlPoints; combParams.resultCount = numPatchPoints - numControlPoints; combParams.resultData = pointData + pointDesc.stride * numControlPoints; combParams.weightData = irregPatch.GetStencilMatrix(); PointCombiner::Apply(combParams); } // // Methods for computing the extent of the control points: // template void Surface::BoundControlPoints( REAL const controlPoints[], PointDescriptor const & pointDesc, REAL boundMin[], REAL boundMax[]) const { int numPoints = GetNumControlPoints(); int pointSize = pointDesc.size; REAL const * p = controlPoints; std::memcpy(boundMin, p, pointSize * sizeof(REAL)); std::memcpy(boundMax, p, pointSize * sizeof(REAL)); for (int i = 1; i < numPoints; ++i) { p += pointDesc.stride; for (int j = 0; j < pointSize; ++j) { boundMin[j] = std::min(boundMin[j], p[j]); boundMax[j] = std::max(boundMax[j], p[j]); } } } template void Surface::BoundControlPointsFromMesh( REAL const meshPoints[], PointDescriptor const & pointDesc, REAL boundMin[], REAL boundMax[]) const { int numPoints = GetNumControlPoints(); int pointSize = pointDesc.size; int const * meshIndices = _data.getCVIndices(); REAL const * p = meshPoints + pointDesc.stride * meshIndices[0]; std::memcpy(boundMin, p, pointSize * sizeof(REAL)); std::memcpy(boundMax, p, pointSize * sizeof(REAL)); for (int i = 1; i < numPoints; ++i) { p = meshPoints + pointDesc.stride * meshIndices[i]; for (int j = 0; j < pointSize; ++j) { boundMin[j] = std::min(boundMin[j], p[j]); boundMax[j] = std::max(boundMax[j], p[j]); } } } // // Internal helper for evaluation: // namespace { template inline int assignWeightsPerDeriv(REAL * const deriv[6], int wSize, REAL wBuffer[], REAL * wDeriv[6]) { std::memset(wDeriv, 0, 6 * sizeof(REAL*)); wDeriv[0] = wBuffer; if (deriv[1] && deriv[2]) { wDeriv[1] = wDeriv[0] + wSize; wDeriv[2] = wDeriv[1] + wSize; if (deriv[3] && deriv[4] && deriv[5]) { wDeriv[3] = wDeriv[2] + wSize; wDeriv[4] = wDeriv[3] + wSize; wDeriv[5] = wDeriv[4] + wSize; return 6; } return 3; } return 1; } } // // Evaluation methods accessing the local data for a simple regular patch: // template void Surface::evalRegularBasis(REAL const uv[2], REAL * wDeriv[]) const { Far::PatchParam patchParam; patchParam.Set(0, 0, 0, 0, 0, getRegPatchMask(), 0, true); Far::internal::EvaluatePatchBasisNormalized( getRegPatchType(), patchParam, uv[0], uv[1], wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]); } template int Surface::evalRegularStencils(REAL const uv[2], REAL * sDeriv[]) const { // // The control points of a regular patch are always the full set // of points required by a patch, i.e. phantom points will have an // entry of some kind (a duplicate). For example, for an isolated // quad, its regular patch still has 16 control points. So we can // return the basis weights as stencil weights for all cases. // Far::PatchParam patchParam; patchParam.Set(0, 0, 0, 0, 0, getRegPatchMask(), 0, true); Far::internal::EvaluatePatchBasisNormalized( getRegPatchType(), patchParam, uv[0], uv[1], sDeriv[0], sDeriv[1], sDeriv[2], sDeriv[3], sDeriv[4], sDeriv[5]); return GetNumControlPoints(); } template void Surface::evalRegularDerivs(REAL const uv[2], REAL const patchPoints[], PointDescriptor const & pointDesc, REAL * deriv[]) const { // // Regular basis evaluation simply returns weights for use with // the entire set of patch control points. // // Assign weights for requested derivatives and evaluate: // REAL wBuffer[6 * 20]; REAL * wDeriv[6]; int numDerivs = assignWeightsPerDeriv(deriv, 20, wBuffer, wDeriv); evalRegularBasis(uv, wDeriv); // // Assemble parameters of the point combination operation and apply: // points::CommonCombinationParameters combineParams; combineParams.pointData = patchPoints; combineParams.pointSize = pointDesc.size; combineParams.pointStride = pointDesc.stride; combineParams.srcCount = GetNumControlPoints(); combineParams.srcIndices = 0; combineParams.resultCount = numDerivs; combineParams.resultArray = deriv; combineParams.weightArray = wDeriv; if (numDerivs == 1) { points::Combine1::Apply(combineParams); } else if (numDerivs == 3) { points::Combine3::Apply(combineParams); } else { points::CombineMultiple::Apply(combineParams); } } // // Evaluation methods accessing the PatchTree for irregular patches: // template typename Surface::IndexArray Surface::evalIrregularBasis(REAL const UV[2], REAL * wDeriv[]) const { Parameterization param = GetParameterization(); REAL uv[2] = { UV[0], UV[1] }; int subFace = param.HasSubFaces() ? param.ConvertCoordToNormalizedSubFace(uv, uv) : 0; internal::IrregularPatchType const & irregPatch = getIrregPatch(); int subPatchIndex = irregPatch.FindSubPatch(uv[0], uv[1], subFace); assert(subPatchIndex >= 0); irregPatch.EvalSubPatchBasis(subPatchIndex, uv[0], uv[1], wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]); return irregPatch.GetSubPatchPoints(subPatchIndex); } template int Surface::evalIrregularStencils(REAL const UV[2], REAL * sDeriv[]) const { Parameterization param = GetParameterization(); REAL uv[2] = { UV[0], UV[1] }; int subFace = param.HasSubFaces() ? param.ConvertCoordToNormalizedSubFace(uv, uv) : 0; internal::IrregularPatchType const & irregPatch = getIrregPatch(); int subPatchIndex = irregPatch.FindSubPatch(uv[0], uv[1], subFace); assert(subPatchIndex >= 0); return irregPatch.EvalSubPatchStencils( subPatchIndex, uv[0], uv[1], sDeriv[0], sDeriv[1], sDeriv[2], sDeriv[3], sDeriv[4], sDeriv[5]); } template void Surface::evalIrregularDerivs(REAL const uv[2], REAL const patchPoints[], PointDescriptor const & pointDesc, REAL * deriv[]) const { // // Non-linear irregular basis evaluation returns both the weights // and the corresponding points of a sub-patch defined by a subset // of the given patch points. // // Assign weights for requested derivatives and evaluate: // REAL wBuffer[6 * 20]; REAL * wDeriv[6]; int numDerivs = assignWeightsPerDeriv(deriv, 20, wBuffer, wDeriv); IndexArray indices = evalIrregularBasis(uv, wDeriv); // // Assemble parameters of the point combination operation and apply: // points::CommonCombinationParameters combineParams; combineParams.pointData = patchPoints; combineParams.pointSize = pointDesc.size; combineParams.pointStride = pointDesc.stride; combineParams.srcCount = indices.size(); combineParams.srcIndices = &indices[0]; combineParams.resultCount = numDerivs; combineParams.resultArray = deriv; combineParams.weightArray = wDeriv; if (numDerivs == 1) { points::Combine1::Apply(combineParams); } else if (numDerivs == 3) { points::Combine3::Apply(combineParams); } else { points::CombineMultiple::Apply(combineParams); } } // // Supporting methods for the N-sided quadrangulated linear patch: // namespace { // // For stencils, there are four unique weights derived from the // four bilinear weights of the sub-face. Given these weights as // input for a sub-face with origin at base point P, the resulting // weights are associated with the N base points as follows: // // w[0] = the point at the origin (P) // w[1] = the point following P // w[2] = the N-3 points not adjacent to P (contributing to center) // w[3] = the point preceding P // template inline void transformLinearQuadWeightsToStencil(REAL w[4], int N) { REAL wOrigin = w[0]; REAL wNext = w[1] * 0.5f; REAL wCenter = w[2] / (REAL)N; REAL wPrev = w[3] * 0.5f; w[0] = wCenter + wNext + wPrev + wOrigin; w[1] = wCenter + wNext; w[2] = wCenter; w[3] = wCenter + wPrev; } template inline void scaleWeights4(REAL w[4], REAL derivScale) { if (w) { w[0] *= derivScale; w[1] *= derivScale; w[2] *= derivScale; w[3] *= derivScale; } } } template int Surface::evalMultiLinearBasis(REAL const UV[2], REAL *wDeriv[]) const { Parameterization param = GetParameterization(); assert(param.GetType() == Parameterization::QUAD_SUBFACES); REAL uv[2]; int subFace = param.ConvertCoordToNormalizedSubFace(UV, uv); // WIP - Prefer to eval Linear basis directly, i.e.: // // Far::internal::EvalBasisLinear(u, v, wP, wDu, wDv); // // but this internal Far function is sometimes optimized out, causing // link errors. Need to fix in Far with explicit instantiation... Far::internal::EvaluatePatchBasisNormalized(Far::PatchDescriptor::QUADS, Far::PatchParam(), uv[0], uv[1], wDeriv[0], wDeriv[1], wDeriv[2], wDeriv[3], wDeriv[4], wDeriv[5]); // Scale weights for derivatives (only mixed partial of 2nd is non-zero): scaleWeights4(wDeriv[1], 2.0f); scaleWeights4(wDeriv[2], 2.0f); scaleWeights4(wDeriv[4], 4.0f); return subFace; } template int Surface::evalMultiLinearStencils(REAL const uv[2], REAL *sDeriv[]) const { // // Linear evaluation of irregular N-sided faces evaluates one of N // locally subdivided quad faces and identifies that sub-face -- also // the origin vertex of the quad. The basis weights are subsequently // transformed into the four unique values that are then assigned to // the N vertices of the face. // // Assign weights for requested stencils and evaluate: // REAL wBuffer[6 * 4]; REAL * wDeriv[6]; int numDerivs = assignWeightsPerDeriv(sDeriv, 4, wBuffer, wDeriv); int iOrigin = evalMultiLinearBasis(uv, wDeriv); // // Transform the four linear weights to four unique stencil weights: // int numControlPoints = GetNumControlPoints(); transformLinearQuadWeightsToStencil(wDeriv[0], numControlPoints); if (numDerivs > 1) { transformLinearQuadWeightsToStencil(wDeriv[1], numControlPoints); transformLinearQuadWeightsToStencil(wDeriv[2], numControlPoints); if (numDerivs > 3) { transformLinearQuadWeightsToStencil(wDeriv[4], numControlPoints); } } // // Assign the N stencil weights from the four unique values: // int iNext = (iOrigin + 1) % numControlPoints; int iPrev = (iOrigin + numControlPoints - 1) % numControlPoints; for (int i = 0; i < numControlPoints; ++i) { int wIndex = 2; if (i == iOrigin) { wIndex = 0; } else if (i == iNext) { wIndex = 1; } else if (i == iPrev) { wIndex = 3; } sDeriv[0][i] = wDeriv[0][wIndex]; if (numDerivs > 1) { sDeriv[1][i] = wDeriv[1][wIndex]; sDeriv[2][i] = wDeriv[2][wIndex]; if (numDerivs > 3) { sDeriv[3][i] = 0.0f; sDeriv[4][i] = wDeriv[4][wIndex]; sDeriv[5][i] = 0.0f; } } } return numControlPoints; } template void Surface::evalMultiLinearDerivs(REAL const uv[], REAL const patchPoints[], PointDescriptor const & pointDesc, REAL * deriv[]) const { // // Linear evaluation of irregular N-sided faces evaluates one of N // locally subdivided quad faces and identifies that sub-face. // // Assign weights for requested derivatives and evaluate: // REAL wBuffer[6 * 4]; REAL * wDeriv[6]; int numDerivs = assignWeightsPerDeriv(deriv, 4, wBuffer, wDeriv); int subQuad = evalMultiLinearBasis(uv, wDeriv); // // Identify the patch points for the sub-face and interpolate: // int N = GetNumControlPoints(); int quadIndices[4]; quadIndices[0] = subQuad; quadIndices[1] = N + 1 + subQuad; quadIndices[2] = N; quadIndices[3] = N + 1 + (subQuad + N - 1) % N; // // Assemble parameters of the point combination operation and apply: // points::CommonCombinationParameters combineParams; combineParams.pointData = patchPoints; combineParams.pointSize = pointDesc.size; combineParams.pointStride = pointDesc.stride; combineParams.srcCount = 4; combineParams.srcIndices = quadIndices; combineParams.resultCount = numDerivs; combineParams.resultArray = deriv; combineParams.weightArray = wDeriv; if (numDerivs == 1) { points::Combine1::Apply(combineParams); } else if (numDerivs == 3) { points::Combine3::Apply(combineParams); } else { points::CombineMultiple::Apply(combineParams); } } // // Public methods to apply stencils: // template void Surface::ApplyStencilFromMesh(REAL const stencil[], REAL const meshPoints[], PointDescriptor const & pointDesc, REAL result[]) const { // // Assemble parameters of the point combination operation and apply: // typedef points::Combine1 PointCombiner; typename PointCombiner::Parameters combParams; combParams.pointData = meshPoints; combParams.pointSize = pointDesc.size; combParams.pointStride = pointDesc.stride; combParams.srcCount = GetNumControlPoints(); combParams.srcIndices = _data.getCVIndices(); combParams.resultCount = 1; combParams.resultArray = &result; combParams.weightArray = &stencil; PointCombiner::Apply(combParams); } template void Surface::ApplyStencil(REAL const stencil[], REAL const controlPoints[], PointDescriptor const & pointDesc, REAL result[]) const { // // Assemble parameters of the point combination operation and apply: // typedef points::Combine1 PointCombiner; typename PointCombiner::Parameters combParams; combParams.pointData = controlPoints; combParams.pointSize = pointDesc.size; combParams.pointStride = pointDesc.stride; combParams.srcCount = GetNumControlPoints(); combParams.srcIndices = 0; combParams.resultCount = 1; combParams.resultArray = &result; combParams.weightArray = &stencil; PointCombiner::Apply(combParams); } // // Explicitly instantiate Surface<> implementations for float and double: // template class Surface; template class Surface; // // Explicitly instantiate template methods for converting precision: // template void Surface::GatherControlPoints( float const [], PointDescriptor const &, float [], PointDescriptor const &) const; template void Surface::GatherControlPoints( double const [], PointDescriptor const &, float [], PointDescriptor const &) const; template void Surface::GatherControlPoints( double const [], PointDescriptor const &, double [], PointDescriptor const &) const; template void Surface::GatherControlPoints( float const [], PointDescriptor const &, double [], PointDescriptor const &) const; } // end namespace Bfr } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv