// // Copyright 2022 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_BFR_POINT_OPERATIONS_H #define OPENSUBDIV3_BFR_POINT_OPERATIONS_H #include "../version.h" #include #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Bfr { // // Internal utilities for efficiently dealing with single and multiple // floating point tuples, i.e. "points": // namespace points { // // Simple classes for primitive point operations -- to be specialized for // small, fixed sizes via template . // // These class templates with static methods are partially specialized for // SIZE. The copy operation is made a separate class due to its additional // template parameters (different precision for source and destination). // Combining the two can lead to undesired ambiguities with the desired // partial specializations. // template struct PointBuilder { static void Set(REAL pDst[], REAL w, REAL const pSrc[], int size) { for (int i = 0; i < size; ++i) { pDst[i] = w * pSrc[i]; } } static void Add(REAL pDst[], REAL w, REAL const pSrc[], int size) { for (int i = 0; i < size; ++i) { pDst[i] += w * pSrc[i]; } } }; template struct PointCopier { static void Copy(REAL_DST * pDst, REAL_SRC const * pSrc, int size) { for (int i = 0; i < size; ++i) { pDst[i] = (REAL_DST) pSrc[i]; } } }; // Specialization for SIZE = 1: template struct PointBuilder { static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] = w * pSrc[0]; } static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] += w * pSrc[0]; } }; template struct PointCopier { static void Copy(REAL * pDst, REAL const * pSrc, int) { pDst[0] = pSrc[0]; } }; // Specialization for SIZE = 2: template struct PointBuilder { static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] = w * pSrc[0]; pDst[1] = w * pSrc[1]; } static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] += w * pSrc[0]; pDst[1] += w * pSrc[1]; } }; template struct PointCopier { static void Copy(REAL * pDst, REAL const * pSrc, int) { pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; } }; // Specialization for SIZE = 3: template struct PointBuilder { static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] = w * pSrc[0]; pDst[1] = w * pSrc[1]; pDst[2] = w * pSrc[2]; } static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] += w * pSrc[0]; pDst[1] += w * pSrc[1]; pDst[2] += w * pSrc[2]; } }; template struct PointCopier { static void Copy(REAL * pDst, REAL const * pSrc, int) { pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; } }; // Specialization for SIZE = 4: template struct PointBuilder { static void Set(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] = w * pSrc[0]; pDst[1] = w * pSrc[1]; pDst[2] = w * pSrc[2]; pDst[3] = w * pSrc[3]; } static void Add(REAL * pDst, REAL w, REAL const * pSrc, int) { pDst[0] += w * pSrc[0]; pDst[1] += w * pSrc[1]; pDst[2] += w * pSrc[2]; pDst[3] += w * pSrc[3]; } }; template struct PointCopier { static void Copy(REAL * pDst, REAL const * pSrc, int) { pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; pDst[3] = pSrc[3]; } }; // Additional specialization for copy when precision matches: template struct PointCopier { static void Copy(REAL * pDst, REAL const * pSrc, int size) { std::memcpy(pDst, pSrc, size * sizeof(REAL)); } }; // // Each major operation is encapsulated in a separate class consisting of // the following: // // - a struct containing all parameters of the operation // - a single private method with a generic implementing for all SIZEs // - a public method invoking specializations for small SIZEs // // Further specializations of the private implementation of each operation // are possible. // // // The Parameters for the operations vary slightly (based on the operation // and potentially varying data destinations) but generally consist of the // following: // // - a description of a full set of input points involved, including: // - the data, size and stride of the array of points // // - a description of the subset of the input points used, including // - the number and optional indices for each "source" point // // - a description of the resulting points, including: // - the number of resulting points // - an array of locations or single location for resulting points // - an array of or consecutive weights for all resulting points // // Several operations combining control points for patch evaluation make // use of the same parameters, so these are encapsulated in a common set. // All classes are expected to declare Parameters for their operation -- // even if it is simply a typedef for the set of common parameters. // // Common set of parameters for operations combining points: template struct CommonCombinationParameters { REAL const * pointData; int pointSize; int pointStride; int const * srcIndices; int srcCount; int resultCount; REAL ** resultArray; REAL const * const * weightArray; }; // // Combination of source points into a single result (for use computing // position only, applying single stencils, and other purposes): // template class Combine1 { public: typedef CommonCombinationParameters Parameters; private: template static void apply(Parameters const & args) { typedef struct PointBuilder Point; int pSize = args.pointSize; int pStride = args.pointStride; REAL const * w = args.weightArray[0]; REAL * p = args.resultArray[0]; if (args.srcIndices == 0) { REAL const * pSrc = args.pointData; Point::Set(p, w[0], pSrc, pSize); for (int i = 1; i < args.srcCount; ++i) { pSrc += pStride; Point::Add(p, w[i], pSrc, pSize); } } else { REAL const * pSrc = args.pointData + pStride * args.srcIndices[0]; Point::Set(p, w[0], pSrc, pSize); for (int i = 1; i < args.srcCount; ++i) { pSrc = args.pointData + pStride * args.srcIndices[i]; Point::Add(p, w[i], pSrc, pSize); } } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; // // Combination of source points into three results (for use computing // position and 1st derivatives): // template class Combine3 { public: typedef CommonCombinationParameters Parameters; private: template static void apply(Parameters const & args) { typedef struct PointBuilder Point; int pSize = args.pointSize; int pStride = args.pointStride; REAL const * const * wArray = args.weightArray; REAL ** pArray = args.resultArray; // // Apply each successive control point to all derivatives at once, // rather than computing each derivate independently: // REAL const * pSrc = (args.srcIndices == 0) ? args.pointData : (args.pointData + pStride * args.srcIndices[0]); Point::Set(pArray[0], wArray[0][0], pSrc, pSize); Point::Set(pArray[1], wArray[1][0], pSrc, pSize); Point::Set(pArray[2], wArray[2][0], pSrc, pSize); for (int i = 1; i < args.srcCount; ++i) { pSrc = (args.srcIndices == 0) ? (pSrc + pStride) : (args.pointData + pStride * args.srcIndices[i]); Point::Add(pArray[0], wArray[0][i], pSrc, pSize); Point::Add(pArray[1], wArray[1][i], pSrc, pSize); Point::Add(pArray[2], wArray[2][i], pSrc, pSize); } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; // // Combination of source points into an arbitrary array of results (for // use computing position with all derivatives, i.e. 6 results): // template class CombineMultiple { public: typedef CommonCombinationParameters Parameters; private: template static void apply(Parameters const & args) { typedef struct PointBuilder Point; int pSize = args.pointSize; int pStride = args.pointStride; REAL const * const * wArray = args.weightArray; REAL ** pArray = args.resultArray; // // Apply each successive control point to all derivatives at once, // rather than computing each derivate independently: // REAL const * pSrc = (args.srcIndices == 0) ? args.pointData : (args.pointData + pStride * args.srcIndices[0]); for (int j = 0; j < args.resultCount; ++j) { Point::Set(pArray[j], wArray[j][0], pSrc, pSize); } for (int i = 1; i < args.srcCount; ++i) { pSrc = (args.srcIndices == 0) ? (pSrc + pStride) : (args.pointData + pStride * args.srcIndices[i]); for (int j = 0; j < args.resultCount; ++j) { Point::Add(pArray[j], wArray[j][i], pSrc, pSize); } } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; // // Combination of a subset of N input points into M resulting points // in consecutive memory locations. The weights for the resulting M // points (N for the input points contributing to each result) are // also stored consecutively: // template class CombineConsecutive { public: struct Parameters { REAL const * pointData; int pointSize; int pointStride; int srcCount; int resultCount; REAL * resultData; REAL const * weightData; }; private: template static void apply(Parameters const & args) { typedef struct PointBuilder Point; REAL const * w = args.weightData; REAL * p = args.resultData; for (int i = 0; i < args.resultCount; ++i) { REAL const * pSrc = args.pointData; Point::Set(p, w[0], pSrc, args.pointSize); for (int j = 1; j < args.srcCount; ++j) { pSrc += args.pointStride; Point::Add(p, w[j], pSrc, args.pointSize); } p += args.pointStride; w += args.srcCount; } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; // // Split the N-sided face formed by the N input control points, i.e. // compute the midpoint of the face and the midpoint of each edge -- // to be stored consecutively in the given location for results: // template class SplitFace { public: struct Parameters { REAL const * pointData; int pointSize; int pointStride; int srcCount; REAL * resultData; }; private: template static void apply(Parameters const & args) { typedef struct PointBuilder Point; int N = args.srcCount; REAL invN = 1.0f / (REAL) N; REAL * facePoint = args.resultData; std::memset(facePoint, 0, args.pointSize * sizeof(REAL)); for (int i = 0; i < N; ++i) { int j = (i < (N - 1)) ? (i + 1) : 0; REAL const * pi = args.pointData + args.pointStride * i; REAL const * pj = args.pointData + args.pointStride * j; Point::Add(facePoint, invN, pi, args.pointSize); REAL * edgePoint = args.resultData + args.pointStride * (1 + i); Point::Set(edgePoint, 0.5f, pi, args.pointSize); Point::Add(edgePoint, 0.5f, pj, args.pointSize); } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; // // Copy a subset of N input points -- identified by the indices given for // each -- to the resulting location specified: // template class CopyConsecutive { public: struct Parameters { REAL_SRC const * pointData; int pointSize; int pointStride; int const * srcIndices; int srcCount; REAL_DST * resultData; int resultStride; }; private: template static void apply(Parameters const & args) { typedef struct PointCopier Point; for (int i = 0; i < args.srcCount; ++i) { REAL_DST * pDst = args.resultData + args.resultStride * i; REAL_SRC const * pSrc = args.pointData + args.pointStride * args.srcIndices[i]; Point::Copy(pDst, pSrc, args.pointSize); } } public: static void Apply(Parameters const & parameters) { switch (parameters.pointSize) { case 1: apply<1>(parameters); break; case 2: apply<2>(parameters); break; case 3: apply<3>(parameters); break; case 4: apply<4>(parameters); break; default: apply<>(parameters); break; } } }; } // end namespace points } // end namespace Bfr } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv #endif /* OPENSUBDIV3_BFR_POINT_OPERATIONS_H */