summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorM Henning <drawoc@darkrefraction.com>2023-08-31 01:01:46 -0400
committerAlyssa Rosenzweig <alyssa@rosenzweig.io>2025-04-16 20:23:20 +0000
commit07a927b3ea33ffad49cccf7b801b0b1ffc336598 (patch)
tree157c09cf17bab3450a4769ba5ee7230163cfd34d
parent9b785af75672c8ce96be8cc369ac2ce51000cbae (diff)
Add shaders from OpenSubdiv
From https://github.com/PixarAnimationStudios/OpenSubdiv tag v3_5_1 From running bin/glViewer and toggling through the compute backends Acked-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
-rw-r--r--COPYING26
-rw-r--r--licenses/APACHE-2.0202
-rw-r--r--shaders/open-subdiv/13.shader_test25415
-rw-r--r--shaders/open-subdiv/21.shader_test35
-rw-r--r--shaders/open-subdiv/22.shader_test2305
-rw-r--r--shaders/open-subdiv/24.shader_test2305
-rw-r--r--shaders/open-subdiv/26.shader_test2285
-rw-r--r--shaders/open-subdiv/28.shader_test2285
-rw-r--r--shaders/open-subdiv/3.shader_test29
-rw-r--r--shaders/open-subdiv/6.shader_test22
-rw-r--r--shaders/open-subdiv/7.shader_test25442
11 files changed, 60351 insertions, 0 deletions
diff --git a/COPYING b/COPYING
index 2bae19b..ac5226e 100644
--- a/COPYING
+++ b/COPYING
@@ -264,3 +264,29 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+shaders/open-subdiv:
+//
+// Copyright 2018 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.
+//
+A copy of the apache license is provided in licenses/APACHE-2.0
diff --git a/licenses/APACHE-2.0 b/licenses/APACHE-2.0
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/licenses/APACHE-2.0
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/shaders/open-subdiv/13.shader_test b/shaders/open-subdiv/13.shader_test
new file mode 100644
index 0000000..b2370a0
--- /dev/null
+++ b/shaders/open-subdiv/13.shader_test
@@ -0,0 +1,25415 @@
+[require]
+GLSL >= 4.60
+
+[vertex shader]
+#version 460
+#define PRIM_TRI
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER
+//
+// Copyright 2015 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} outpt[20];
+
+layout(vertices = 20) out;
+
+void main()
+{
+ vec3 cv = inpt[gl_InvocationID].v.position.xyz;
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexGregoryBasis(
+ patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(20);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier bezcv[16];
+ bezcv[ 0].P = inpt[ 0].v.position.xyz;
+ bezcv[ 1].P = inpt[ 1].v.position.xyz;
+ bezcv[ 2].P = inpt[ 7].v.position.xyz;
+ bezcv[ 3].P = inpt[ 5].v.position.xyz;
+ bezcv[ 4].P = inpt[ 2].v.position.xyz;
+ bezcv[ 5].P = inpt[ 3].v.position.xyz;
+ bezcv[ 6].P = inpt[ 8].v.position.xyz;
+ bezcv[ 7].P = inpt[ 6].v.position.xyz;
+ bezcv[ 8].P = inpt[16].v.position.xyz;
+ bezcv[ 9].P = inpt[18].v.position.xyz;
+ bezcv[10].P = inpt[13].v.position.xyz;
+ bezcv[11].P = inpt[12].v.position.xyz;
+ bezcv[12].P = inpt[15].v.position.xyz;
+ bezcv[13].P = inpt[17].v.position.xyz;
+ bezcv[14].P = inpt[11].v.position.xyz;
+ bezcv[15].P = inpt[10].v.position.xyz;
+
+ OsdEvalPatchBezierTessLevels(
+ bezcv, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(
+ patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ vec3 cv[20];
+ for (int i = 0; i < 20; ++i) {
+ cv[i] = inpt[i].v.P;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchGregory(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 0, 5, 15, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[tessellation control shader]
+#version 460
+#define PRIM_TRI
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER
+//
+// Copyright 2015 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} outpt[20];
+
+layout(vertices = 20) out;
+
+void main()
+{
+ vec3 cv = inpt[gl_InvocationID].v.position.xyz;
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexGregoryBasis(
+ patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(20);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier bezcv[16];
+ bezcv[ 0].P = inpt[ 0].v.position.xyz;
+ bezcv[ 1].P = inpt[ 1].v.position.xyz;
+ bezcv[ 2].P = inpt[ 7].v.position.xyz;
+ bezcv[ 3].P = inpt[ 5].v.position.xyz;
+ bezcv[ 4].P = inpt[ 2].v.position.xyz;
+ bezcv[ 5].P = inpt[ 3].v.position.xyz;
+ bezcv[ 6].P = inpt[ 8].v.position.xyz;
+ bezcv[ 7].P = inpt[ 6].v.position.xyz;
+ bezcv[ 8].P = inpt[16].v.position.xyz;
+ bezcv[ 9].P = inpt[18].v.position.xyz;
+ bezcv[10].P = inpt[13].v.position.xyz;
+ bezcv[11].P = inpt[12].v.position.xyz;
+ bezcv[12].P = inpt[15].v.position.xyz;
+ bezcv[13].P = inpt[17].v.position.xyz;
+ bezcv[14].P = inpt[11].v.position.xyz;
+ bezcv[15].P = inpt[10].v.position.xyz;
+
+ OsdEvalPatchBezierTessLevels(
+ bezcv, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(
+ patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ vec3 cv[20];
+ for (int i = 0; i < 20; ++i) {
+ cv[i] = inpt[i].v.P;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchGregory(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 0, 5, 15, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[tessellation evaluation shader]
+#version 460
+#define PRIM_TRI
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER
+//
+// Copyright 2015 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} outpt[20];
+
+layout(vertices = 20) out;
+
+void main()
+{
+ vec3 cv = inpt[gl_InvocationID].v.position.xyz;
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexGregoryBasis(
+ patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(20);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier bezcv[16];
+ bezcv[ 0].P = inpt[ 0].v.position.xyz;
+ bezcv[ 1].P = inpt[ 1].v.position.xyz;
+ bezcv[ 2].P = inpt[ 7].v.position.xyz;
+ bezcv[ 3].P = inpt[ 5].v.position.xyz;
+ bezcv[ 4].P = inpt[ 2].v.position.xyz;
+ bezcv[ 5].P = inpt[ 3].v.position.xyz;
+ bezcv[ 6].P = inpt[ 8].v.position.xyz;
+ bezcv[ 7].P = inpt[ 6].v.position.xyz;
+ bezcv[ 8].P = inpt[16].v.position.xyz;
+ bezcv[ 9].P = inpt[18].v.position.xyz;
+ bezcv[10].P = inpt[13].v.position.xyz;
+ bezcv[11].P = inpt[12].v.position.xyz;
+ bezcv[12].P = inpt[15].v.position.xyz;
+ bezcv[13].P = inpt[17].v.position.xyz;
+ bezcv[14].P = inpt[11].v.position.xyz;
+ bezcv[15].P = inpt[10].v.position.xyz;
+
+ OsdEvalPatchBezierTessLevels(
+ bezcv, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(
+ patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalGregoryBasis
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexGregoryBasis v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ vec3 cv[20];
+ for (int i = 0; i < 20; ++i) {
+ cv[i] = inpt[i].v.P;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchGregory(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 0, 5, 15, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[geometry shader]
+#version 460
+#define PRIM_TRI
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+#define GEOMETRY_SHADER
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+
+[fragment shader]
+#version 460
+#define PRIM_TRI
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_GREGORY_BASIS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+#define FRAGMENT_SHADER
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+
diff --git a/shaders/open-subdiv/21.shader_test b/shaders/open-subdiv/21.shader_test
new file mode 100644
index 0000000..0f70503
--- /dev/null
+++ b/shaders/open-subdiv/21.shader_test
@@ -0,0 +1,35 @@
+[require]
+GLSL >= 4.60
+
+[vertex shader]
+#version 460
+in vec3 position;
+in float vertSharpness;
+out float sharpness;
+uniform mat4 mvpMatrix;
+void main() {
+ sharpness = vertSharpness;
+ gl_Position = mvpMatrix * vec4(position, 1);
+}
+
+[fragment shader]
+#version 460
+in float sharpness;
+out vec4 color;
+uniform int drawMode = 0;
+uniform samplerBuffer edgeSharpness;
+vec4 sharpnessToColor(float s) {
+ // 0.0 2.0 4.0
+ // green --- yellow --- red
+ return vec4(min(1, s * 0.5),
+ min(1, 2 - s * 0.5),
+ 0, 1);
+}
+void main() {
+ float sharp = sharpness;
+ if (drawMode == 1) {
+ sharp = texelFetch(edgeSharpness, gl_PrimitiveID).x;
+ }
+ color = sharpnessToColor(sharp);
+}
+
diff --git a/shaders/open-subdiv/22.shader_test b/shaders/open-subdiv/22.shader_test
new file mode 100644
index 0000000..bab471d
--- /dev/null
+++ b/shaders/open-subdiv/22.shader_test
@@ -0,0 +1,2305 @@
+[require]
+GLSL >= 4.30
+
+[compute shader]
+#version 430
+#define LENGTH 3
+#define SRC_STRIDE 3
+#define DST_STRIDE 3
+#define WORK_GROUP_SIZE 64
+#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS
+
+#define OSD_PATCH_BASIS_GLSL
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//------------------------------------------------------------------------------
+
+
+layout(local_size_x=WORK_GROUP_SIZE, local_size_y=1, local_size_z=1) in;
+layout(std430) buffer;
+
+// source and destination buffers
+
+uniform int srcOffset = 0;
+uniform int dstOffset = 0;
+layout(binding=0) buffer src_buffer { float srcVertexBuffer[]; };
+layout(binding=1) buffer dst_buffer { float dstVertexBuffer[]; };
+
+// derivative buffers (if needed)
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+uniform ivec3 duDesc;
+uniform ivec3 dvDesc;
+layout(binding=2) buffer du_buffer { float duBuffer[]; };
+layout(binding=3) buffer dv_buffer { float dvBuffer[]; };
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+uniform ivec3 duuDesc;
+uniform ivec3 duvDesc;
+uniform ivec3 dvvDesc;
+layout(binding=10) buffer duu_buffer { float duuBuffer[]; };
+layout(binding=11) buffer duv_buffer { float duvBuffer[]; };
+layout(binding=12) buffer dvv_buffer { float dvvBuffer[]; };
+#endif
+
+// stencil buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+uniform int batchStart = 0;
+uniform int batchEnd = 0;
+layout(binding=4) buffer stencilSizes { int _sizes[]; };
+layout(binding=5) buffer stencilOffsets { int _offsets[]; };
+layout(binding=6) buffer stencilIndices { int _indices[]; };
+layout(binding=7) buffer stencilWeights { float _weights[]; };
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+layout(binding=8) buffer stencilDuWeights { float _duWeights[]; };
+layout(binding=9) buffer stencilDvWeights { float _dvWeights[]; };
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+layout(binding=13) buffer stencilDuuWeights { float _duuWeights[]; };
+layout(binding=14) buffer stencilDuvWeights { float _duvWeights[]; };
+layout(binding=15) buffer stencilDvvWeights { float _dvvWeights[]; };
+#endif
+
+#endif
+
+// patch buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+layout(binding=4) buffer patchArray_buffer { OsdPatchArray patchArrayBuffer[]; };
+layout(binding=5) buffer patchCoord_buffer { OsdPatchCoord patchCoords[]; };
+layout(binding=6) buffer patchIndex_buffer { int patchIndexBuffer[]; };
+layout(binding=7) buffer patchParam_buffer { OsdPatchParam patchParamBuffer[]; };
+
+OsdPatchCoord GetPatchCoord(int coordIndex)
+{
+ return patchCoords[coordIndex];
+}
+
+OsdPatchArray GetPatchArray(int arrayIndex)
+{
+ return patchArrayBuffer[arrayIndex];
+}
+
+OsdPatchParam GetPatchParam(int patchIndex)
+{
+ return patchParamBuffer[patchIndex];
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+struct Vertex {
+ float vertexData[LENGTH];
+};
+
+void clear(out Vertex v) {
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = 0;
+ }
+}
+
+Vertex readVertex(int index) {
+ Vertex v;
+ int vertexIndex = srcOffset + index * SRC_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
+ }
+ return v;
+}
+
+void writeVertex(int index, Vertex v) {
+ int vertexIndex = dstOffset + index * DST_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
+ }
+}
+
+void addWithWeight(inout Vertex v, const Vertex src, float weight) {
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] += weight * src.vertexData[i];
+ }
+}
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+void writeDu(int index, Vertex du) {
+ int duIndex = duDesc.x + index * duDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duBuffer[duIndex + i] = du.vertexData[i];
+ }
+}
+
+void writeDv(int index, Vertex dv) {
+ int dvIndex = dvDesc.x + index * dvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvBuffer[dvIndex + i] = dv.vertexData[i];
+ }
+}
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+void writeDuu(int index, Vertex duu) {
+ int duuIndex = duuDesc.x + index * duuDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duuBuffer[duuIndex + i] = duu.vertexData[i];
+ }
+}
+
+void writeDuv(int index, Vertex duv) {
+ int duvIndex = duvDesc.x + index * duvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duvBuffer[duvIndex + i] = duv.vertexData[i];
+ }
+}
+
+void writeDvv(int index, Vertex dvv) {
+ int dvvIndex = dvvDesc.x + index * dvvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvvBuffer[dvvIndex + i] = dvv.vertexData[i];
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+void main() {
+
+ int current = int(gl_GlobalInvocationID.x) + batchStart;
+
+ if (current>=batchEnd) {
+ return;
+ }
+
+ Vertex dst;
+ clear(dst);
+
+ int offset = _offsets[current],
+ size = _sizes[current];
+
+ for (int stencil = 0; stencil < size; ++stencil) {
+ int vindex = offset + stencil;
+ addWithWeight(
+ dst, readVertex(_indices[vindex]), _weights[vindex]);
+ }
+
+ writeVertex(current, dst);
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ Vertex du, dv;
+ clear(du);
+ clear(dv);
+ for (int i=0; i<size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset+i]);
+ addWithWeight(du, src, _duWeights[offset+i]);
+ addWithWeight(dv, src, _dvWeights[offset+i]);
+ }
+
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+#endif
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ Vertex duu, duv, dvv;
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+ for (int i=0; i<size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset+i]);
+ addWithWeight(duu, src, _duuWeights[offset+i]);
+ addWithWeight(duv, src, _duvWeights[offset+i]);
+ addWithWeight(dvv, src, _dvvWeights[offset+i]);
+ }
+
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) {
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+#endif
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+// PERFORMANCE: stride could be constant, but not as significant as length
+
+void main() {
+
+ int current = int(gl_GlobalInvocationID.x);
+
+ OsdPatchCoord coord = GetPatchCoord(current);
+ OsdPatchArray array = GetPatchArray(coord.arrayIndex);
+ OsdPatchParam param = GetPatchParam(coord.patchIndex);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int nPoints = OsdEvaluatePatchBasis(patchType, param,
+ coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int indexBase = array.indexBase + array.stride *
+ (coord.patchIndex - array.primitiveIdBase);
+
+ for (int cv = 0; cv < nPoints; ++cv) {
+ int index = patchIndexBuffer[indexBase + cv];
+ addWithWeight(dst, readVertex(index), wP[cv]);
+ addWithWeight(du, readVertex(index), wDu[cv]);
+ addWithWeight(dv, readVertex(index), wDv[cv]);
+ addWithWeight(duu, readVertex(index), wDuu[cv]);
+ addWithWeight(duv, readVertex(index), wDuv[cv]);
+ addWithWeight(dvv, readVertex(index), wDvv[cv]);
+ }
+ writeVertex(current, dst);
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+#endif
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) { // length
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+#endif
+}
+
+#endif
+
+
diff --git a/shaders/open-subdiv/24.shader_test b/shaders/open-subdiv/24.shader_test
new file mode 100644
index 0000000..2fe3881
--- /dev/null
+++ b/shaders/open-subdiv/24.shader_test
@@ -0,0 +1,2305 @@
+[require]
+GLSL >= 4.30
+
+[compute shader]
+#version 430
+#define LENGTH 3
+#define SRC_STRIDE 3
+#define DST_STRIDE 3
+#define WORK_GROUP_SIZE 64
+#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES
+
+#define OSD_PATCH_BASIS_GLSL
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//------------------------------------------------------------------------------
+
+
+layout(local_size_x=WORK_GROUP_SIZE, local_size_y=1, local_size_z=1) in;
+layout(std430) buffer;
+
+// source and destination buffers
+
+uniform int srcOffset = 0;
+uniform int dstOffset = 0;
+layout(binding=0) buffer src_buffer { float srcVertexBuffer[]; };
+layout(binding=1) buffer dst_buffer { float dstVertexBuffer[]; };
+
+// derivative buffers (if needed)
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+uniform ivec3 duDesc;
+uniform ivec3 dvDesc;
+layout(binding=2) buffer du_buffer { float duBuffer[]; };
+layout(binding=3) buffer dv_buffer { float dvBuffer[]; };
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+uniform ivec3 duuDesc;
+uniform ivec3 duvDesc;
+uniform ivec3 dvvDesc;
+layout(binding=10) buffer duu_buffer { float duuBuffer[]; };
+layout(binding=11) buffer duv_buffer { float duvBuffer[]; };
+layout(binding=12) buffer dvv_buffer { float dvvBuffer[]; };
+#endif
+
+// stencil buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+uniform int batchStart = 0;
+uniform int batchEnd = 0;
+layout(binding=4) buffer stencilSizes { int _sizes[]; };
+layout(binding=5) buffer stencilOffsets { int _offsets[]; };
+layout(binding=6) buffer stencilIndices { int _indices[]; };
+layout(binding=7) buffer stencilWeights { float _weights[]; };
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+layout(binding=8) buffer stencilDuWeights { float _duWeights[]; };
+layout(binding=9) buffer stencilDvWeights { float _dvWeights[]; };
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+layout(binding=13) buffer stencilDuuWeights { float _duuWeights[]; };
+layout(binding=14) buffer stencilDuvWeights { float _duvWeights[]; };
+layout(binding=15) buffer stencilDvvWeights { float _dvvWeights[]; };
+#endif
+
+#endif
+
+// patch buffers
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+layout(binding=4) buffer patchArray_buffer { OsdPatchArray patchArrayBuffer[]; };
+layout(binding=5) buffer patchCoord_buffer { OsdPatchCoord patchCoords[]; };
+layout(binding=6) buffer patchIndex_buffer { int patchIndexBuffer[]; };
+layout(binding=7) buffer patchParam_buffer { OsdPatchParam patchParamBuffer[]; };
+
+OsdPatchCoord GetPatchCoord(int coordIndex)
+{
+ return patchCoords[coordIndex];
+}
+
+OsdPatchArray GetPatchArray(int arrayIndex)
+{
+ return patchArrayBuffer[arrayIndex];
+}
+
+OsdPatchParam GetPatchParam(int patchIndex)
+{
+ return patchParamBuffer[patchIndex];
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+struct Vertex {
+ float vertexData[LENGTH];
+};
+
+void clear(out Vertex v) {
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = 0;
+ }
+}
+
+Vertex readVertex(int index) {
+ Vertex v;
+ int vertexIndex = srcOffset + index * SRC_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
+ }
+ return v;
+}
+
+void writeVertex(int index, Vertex v) {
+ int vertexIndex = dstOffset + index * DST_STRIDE;
+ for (int i = 0; i < LENGTH; ++i) {
+ dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
+ }
+}
+
+void addWithWeight(inout Vertex v, const Vertex src, float weight) {
+ for (int i = 0; i < LENGTH; ++i) {
+ v.vertexData[i] += weight * src.vertexData[i];
+ }
+}
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+void writeDu(int index, Vertex du) {
+ int duIndex = duDesc.x + index * duDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duBuffer[duIndex + i] = du.vertexData[i];
+ }
+}
+
+void writeDv(int index, Vertex dv) {
+ int dvIndex = dvDesc.x + index * dvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvBuffer[dvIndex + i] = dv.vertexData[i];
+ }
+}
+#endif
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+void writeDuu(int index, Vertex duu) {
+ int duuIndex = duuDesc.x + index * duuDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duuBuffer[duuIndex + i] = duu.vertexData[i];
+ }
+}
+
+void writeDuv(int index, Vertex duv) {
+ int duvIndex = duvDesc.x + index * duvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ duvBuffer[duvIndex + i] = duv.vertexData[i];
+ }
+}
+
+void writeDvv(int index, Vertex dvv) {
+ int dvvIndex = dvvDesc.x + index * dvvDesc.z;
+ for (int i = 0; i < LENGTH; ++i) {
+ dvvBuffer[dvvIndex + i] = dvv.vertexData[i];
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
+
+void main() {
+
+ int current = int(gl_GlobalInvocationID.x) + batchStart;
+
+ if (current>=batchEnd) {
+ return;
+ }
+
+ Vertex dst;
+ clear(dst);
+
+ int offset = _offsets[current],
+ size = _sizes[current];
+
+ for (int stencil = 0; stencil < size; ++stencil) {
+ int vindex = offset + stencil;
+ addWithWeight(
+ dst, readVertex(_indices[vindex]), _weights[vindex]);
+ }
+
+ writeVertex(current, dst);
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ Vertex du, dv;
+ clear(du);
+ clear(dv);
+ for (int i=0; i<size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset+i]);
+ addWithWeight(du, src, _duWeights[offset+i]);
+ addWithWeight(dv, src, _dvWeights[offset+i]);
+ }
+
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+#endif
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ Vertex duu, duv, dvv;
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+ for (int i=0; i<size; ++i) {
+ // expects the compiler optimizes readVertex out here.
+ Vertex src = readVertex(_indices[offset+i]);
+ addWithWeight(duu, src, _duuWeights[offset+i]);
+ addWithWeight(duv, src, _duvWeights[offset+i]);
+ addWithWeight(dvv, src, _dvvWeights[offset+i]);
+ }
+
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) {
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+#endif
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
+
+// PERFORMANCE: stride could be constant, but not as significant as length
+
+void main() {
+
+ int current = int(gl_GlobalInvocationID.x);
+
+ OsdPatchCoord coord = GetPatchCoord(current);
+ OsdPatchArray array = GetPatchArray(coord.arrayIndex);
+ OsdPatchParam param = GetPatchParam(coord.patchIndex);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int nPoints = OsdEvaluatePatchBasis(patchType, param,
+ coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int indexBase = array.indexBase + array.stride *
+ (coord.patchIndex - array.primitiveIdBase);
+
+ for (int cv = 0; cv < nPoints; ++cv) {
+ int index = patchIndexBuffer[indexBase + cv];
+ addWithWeight(dst, readVertex(index), wP[cv]);
+ addWithWeight(du, readVertex(index), wDu[cv]);
+ addWithWeight(dv, readVertex(index), wDv[cv]);
+ addWithWeight(duu, readVertex(index), wDuu[cv]);
+ addWithWeight(duv, readVertex(index), wDuv[cv]);
+ addWithWeight(dvv, readVertex(index), wDvv[cv]);
+ }
+ writeVertex(current, dst);
+
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
+ if (duDesc.y > 0) { // length
+ writeDu(current, du);
+ }
+ if (dvDesc.y > 0) {
+ writeDv(current, dv);
+ }
+#endif
+#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
+ if (duuDesc.y > 0) { // length
+ writeDuu(current, duu);
+ }
+ if (duvDesc.y > 0) { // length
+ writeDuv(current, duv);
+ }
+ if (dvvDesc.y > 0) {
+ writeDvv(current, dvv);
+ }
+#endif
+}
+
+#endif
+
+
diff --git a/shaders/open-subdiv/26.shader_test b/shaders/open-subdiv/26.shader_test
new file mode 100644
index 0000000..7b4e542
--- /dev/null
+++ b/shaders/open-subdiv/26.shader_test
@@ -0,0 +1,2285 @@
+[require]
+GLSL >= 4.10
+
+[vertex shader]
+#version 410
+#define LENGTH 3
+#define SRC_STRIDE 3
+#define VERTEX_SHADER
+#define OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_STENCILS
+
+#define OSD_PATCH_BASIS_GLSL
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//------------------------------------------------------------------------------
+
+uniform samplerBuffer vertexBuffer;
+uniform int srcOffset = 0;
+out float outVertexBuffer[LENGTH];
+
+//------------------------------------------------------------------------------
+
+struct Vertex {
+ float vertexData[LENGTH];
+};
+
+void clear(out Vertex v) {
+ for (int i = 0; i < LENGTH; i++) {
+ v.vertexData[i] = 0;
+ }
+}
+
+void addWithWeight(inout Vertex v, Vertex src, float weight) {
+ for(int j = 0; j < LENGTH; j++) {
+ v.vertexData[j] += weight * src.vertexData[j];
+ }
+}
+
+Vertex readVertex(int index) {
+ Vertex v;
+ int vertexIndex = srcOffset + index * SRC_STRIDE;
+ for(int j = 0; j < LENGTH; j++) {
+ v.vertexData[j] = texelFetch(vertexBuffer, vertexIndex+j).x;
+ }
+ return v;
+}
+
+void writeVertex(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outVertexBuffer[i] = v.vertexData[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES) && defined(OPENSUBDIV_GLSL_XFB_INTERLEAVED_1ST_DERIVATIVE_BUFFERS)
+out float outDeriv1Buffer[2*LENGTH];
+
+void writeDu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv1Buffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv1Buffer[i+LENGTH] = v.vertexData[i];
+ }
+}
+#elif defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+out float outDuBuffer[LENGTH];
+out float outDvBuffer[LENGTH];
+
+void writeDu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDvBuffer[i] = v.vertexData[i];
+ }
+}
+#endif
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES) && defined(OPENSUBDIV_GLSL_XFB_INTERLEAVED_2ND_DERIVATIVE_BUFFERS)
+out float outDeriv2Buffer[3*LENGTH];
+
+void writeDuu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDuv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i+LENGTH] = v.vertexData[i];
+ }
+}
+
+void writeDvv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i+2*LENGTH] = v.vertexData[i];
+ }
+}
+#elif defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+out float outDuuBuffer[LENGTH];
+out float outDuvBuffer[LENGTH];
+out float outDvvBuffer[LENGTH];
+
+void writeDuu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuuBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDuv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuvBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDvv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDvvBuffer[i] = v.vertexData[i];
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_STENCILS)
+
+uniform usamplerBuffer sizes;
+uniform isamplerBuffer offsets;
+uniform isamplerBuffer indices;
+uniform samplerBuffer weights;
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+uniform samplerBuffer duWeights;
+uniform samplerBuffer dvWeights;
+#endif
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+uniform samplerBuffer duuWeights;
+uniform samplerBuffer duvWeights;
+uniform samplerBuffer dvvWeights;
+#endif
+
+uniform int batchStart = 0;
+uniform int batchEnd = 0;
+
+void main() {
+ int current = gl_VertexID + batchStart;
+
+ if (current>=batchEnd) {
+ return;
+ }
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int offset = texelFetch(offsets, current).x;
+ uint size = texelFetch(sizes, current).x;
+
+ for (int stencil=0; stencil<size; ++stencil) {
+ int index = texelFetch(indices, offset+stencil).x;
+ float weight = texelFetch(weights, offset+stencil).x;
+ addWithWeight(dst, readVertex( index ), weight);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ float duWeight = texelFetch(duWeights, offset+stencil).x;
+ float dvWeight = texelFetch(dvWeights, offset+stencil).x;
+ addWithWeight(du, readVertex(index), duWeight);
+ addWithWeight(dv, readVertex(index), dvWeight);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ float duuWeight = texelFetch(duuWeights, offset+stencil).x;
+ float duvWeight = texelFetch(duvWeights, offset+stencil).x;
+ float dvvWeight = texelFetch(dvvWeights, offset+stencil).x;
+ addWithWeight(duu, readVertex(index), duuWeight);
+ addWithWeight(duv, readVertex(index), duvWeight);
+ addWithWeight(dvv, readVertex(index), dvvWeight);
+#endif
+ }
+ writeVertex(dst);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ writeDu(du);
+ writeDv(dv);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ writeDuu(duu);
+ writeDuv(duv);
+ writeDvv(dvv);
+#endif
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_PATCHES)
+
+layout (location = 0) in ivec3 patchHandles;
+layout (location = 1) in vec2 patchCoords;
+
+layout (std140) uniform PatchArrays {
+ OsdPatchArray patchArrays[2];
+};
+uniform isamplerBuffer patchParamBuffer;
+uniform isamplerBuffer patchIndexBuffer;
+
+OsdPatchArray GetPatchArray(int arrayIndex) {
+ return patchArrays[arrayIndex];
+}
+
+OsdPatchParam GetPatchParam(int patchIndex) {
+ ivec3 patchParamBits = texelFetch(patchParamBuffer, patchIndex).xyz;
+ return OsdPatchParamInit(patchParamBits.x, patchParamBits.y, patchParamBits.z);
+}
+
+void main() {
+ int current = gl_VertexID;
+
+ ivec3 handle = patchHandles;
+ int arrayIndex = handle.x;
+ int patchIndex = handle.y;
+
+ vec2 coord = patchCoords;
+
+ OsdPatchArray array = GetPatchArray(arrayIndex);
+ OsdPatchParam param = GetPatchParam(patchIndex);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int nPoints = OsdEvaluatePatchBasis(patchType, param,
+ coord.x, coord.y, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int indexBase = array.indexBase + array.stride *
+ (patchIndex - array.primitiveIdBase);
+
+ for (int cv = 0; cv < nPoints; ++cv) {
+ int index = texelFetch(patchIndexBuffer, indexBase + cv).x;
+ addWithWeight(dst, readVertex(index), wP[cv]);
+ addWithWeight(du, readVertex(index), wDu[cv]);
+ addWithWeight(dv, readVertex(index), wDv[cv]);
+ addWithWeight(duu, readVertex(index), wDuu[cv]);
+ addWithWeight(duv, readVertex(index), wDuv[cv]);
+ addWithWeight(dvv, readVertex(index), wDvv[cv]);
+ }
+
+ writeVertex(dst);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ writeDu(du);
+ writeDv(dv);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ writeDuu(duu);
+ writeDuv(duv);
+ writeDvv(dvv);
+#endif
+}
+
+#endif
+
+
+
diff --git a/shaders/open-subdiv/28.shader_test b/shaders/open-subdiv/28.shader_test
new file mode 100644
index 0000000..3acba04
--- /dev/null
+++ b/shaders/open-subdiv/28.shader_test
@@ -0,0 +1,2285 @@
+[require]
+GLSL >= 4.10
+
+[vertex shader]
+#version 410
+#define LENGTH 3
+#define SRC_STRIDE 3
+#define VERTEX_SHADER
+#define OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_PATCHES
+
+#define OSD_PATCH_BASIS_GLSL
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//------------------------------------------------------------------------------
+
+uniform samplerBuffer vertexBuffer;
+uniform int srcOffset = 0;
+out float outVertexBuffer[LENGTH];
+
+//------------------------------------------------------------------------------
+
+struct Vertex {
+ float vertexData[LENGTH];
+};
+
+void clear(out Vertex v) {
+ for (int i = 0; i < LENGTH; i++) {
+ v.vertexData[i] = 0;
+ }
+}
+
+void addWithWeight(inout Vertex v, Vertex src, float weight) {
+ for(int j = 0; j < LENGTH; j++) {
+ v.vertexData[j] += weight * src.vertexData[j];
+ }
+}
+
+Vertex readVertex(int index) {
+ Vertex v;
+ int vertexIndex = srcOffset + index * SRC_STRIDE;
+ for(int j = 0; j < LENGTH; j++) {
+ v.vertexData[j] = texelFetch(vertexBuffer, vertexIndex+j).x;
+ }
+ return v;
+}
+
+void writeVertex(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outVertexBuffer[i] = v.vertexData[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES) && defined(OPENSUBDIV_GLSL_XFB_INTERLEAVED_1ST_DERIVATIVE_BUFFERS)
+out float outDeriv1Buffer[2*LENGTH];
+
+void writeDu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv1Buffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv1Buffer[i+LENGTH] = v.vertexData[i];
+ }
+}
+#elif defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+out float outDuBuffer[LENGTH];
+out float outDvBuffer[LENGTH];
+
+void writeDu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDvBuffer[i] = v.vertexData[i];
+ }
+}
+#endif
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES) && defined(OPENSUBDIV_GLSL_XFB_INTERLEAVED_2ND_DERIVATIVE_BUFFERS)
+out float outDeriv2Buffer[3*LENGTH];
+
+void writeDuu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDuv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i+LENGTH] = v.vertexData[i];
+ }
+}
+
+void writeDvv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDeriv2Buffer[i+2*LENGTH] = v.vertexData[i];
+ }
+}
+#elif defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+out float outDuuBuffer[LENGTH];
+out float outDuvBuffer[LENGTH];
+out float outDvvBuffer[LENGTH];
+
+void writeDuu(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuuBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDuv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDuvBuffer[i] = v.vertexData[i];
+ }
+}
+
+void writeDvv(Vertex v) {
+ for(int i = 0; i < LENGTH; i++) {
+ outDvvBuffer[i] = v.vertexData[i];
+ }
+}
+#endif
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_STENCILS)
+
+uniform usamplerBuffer sizes;
+uniform isamplerBuffer offsets;
+uniform isamplerBuffer indices;
+uniform samplerBuffer weights;
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+uniform samplerBuffer duWeights;
+uniform samplerBuffer dvWeights;
+#endif
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+uniform samplerBuffer duuWeights;
+uniform samplerBuffer duvWeights;
+uniform samplerBuffer dvvWeights;
+#endif
+
+uniform int batchStart = 0;
+uniform int batchEnd = 0;
+
+void main() {
+ int current = gl_VertexID + batchStart;
+
+ if (current>=batchEnd) {
+ return;
+ }
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int offset = texelFetch(offsets, current).x;
+ uint size = texelFetch(sizes, current).x;
+
+ for (int stencil=0; stencil<size; ++stencil) {
+ int index = texelFetch(indices, offset+stencil).x;
+ float weight = texelFetch(weights, offset+stencil).x;
+ addWithWeight(dst, readVertex( index ), weight);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ float duWeight = texelFetch(duWeights, offset+stencil).x;
+ float dvWeight = texelFetch(dvWeights, offset+stencil).x;
+ addWithWeight(du, readVertex(index), duWeight);
+ addWithWeight(dv, readVertex(index), dvWeight);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ float duuWeight = texelFetch(duuWeights, offset+stencil).x;
+ float duvWeight = texelFetch(duvWeights, offset+stencil).x;
+ float dvvWeight = texelFetch(dvvWeights, offset+stencil).x;
+ addWithWeight(duu, readVertex(index), duuWeight);
+ addWithWeight(duv, readVertex(index), duvWeight);
+ addWithWeight(dvv, readVertex(index), dvvWeight);
+#endif
+ }
+ writeVertex(dst);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ writeDu(du);
+ writeDv(dv);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ writeDuu(duu);
+ writeDuv(duv);
+ writeDvv(dvv);
+#endif
+}
+
+#endif
+
+//------------------------------------------------------------------------------
+
+#if defined(OPENSUBDIV_GLSL_XFB_KERNEL_EVAL_PATCHES)
+
+layout (location = 0) in ivec3 patchHandles;
+layout (location = 1) in vec2 patchCoords;
+
+layout (std140) uniform PatchArrays {
+ OsdPatchArray patchArrays[2];
+};
+uniform isamplerBuffer patchParamBuffer;
+uniform isamplerBuffer patchIndexBuffer;
+
+OsdPatchArray GetPatchArray(int arrayIndex) {
+ return patchArrays[arrayIndex];
+}
+
+OsdPatchParam GetPatchParam(int patchIndex) {
+ ivec3 patchParamBits = texelFetch(patchParamBuffer, patchIndex).xyz;
+ return OsdPatchParamInit(patchParamBits.x, patchParamBits.y, patchParamBits.z);
+}
+
+void main() {
+ int current = gl_VertexID;
+
+ ivec3 handle = patchHandles;
+ int arrayIndex = handle.x;
+ int patchIndex = handle.y;
+
+ vec2 coord = patchCoords;
+
+ OsdPatchArray array = GetPatchArray(arrayIndex);
+ OsdPatchParam param = GetPatchParam(patchIndex);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int nPoints = OsdEvaluatePatchBasis(patchType, param,
+ coord.x, coord.y, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ Vertex dst, du, dv, duu, duv, dvv;
+ clear(dst);
+ clear(du);
+ clear(dv);
+ clear(duu);
+ clear(duv);
+ clear(dvv);
+
+ int indexBase = array.indexBase + array.stride *
+ (patchIndex - array.primitiveIdBase);
+
+ for (int cv = 0; cv < nPoints; ++cv) {
+ int index = texelFetch(patchIndexBuffer, indexBase + cv).x;
+ addWithWeight(dst, readVertex(index), wP[cv]);
+ addWithWeight(du, readVertex(index), wDu[cv]);
+ addWithWeight(dv, readVertex(index), wDv[cv]);
+ addWithWeight(duu, readVertex(index), wDuu[cv]);
+ addWithWeight(duv, readVertex(index), wDuv[cv]);
+ addWithWeight(dvv, readVertex(index), wDvv[cv]);
+ }
+
+ writeVertex(dst);
+
+#if defined(OPENSUBDIV_GLSL_XFB_USE_1ST_DERIVATIVES)
+ writeDu(du);
+ writeDv(dv);
+#endif
+#if defined(OPENSUBDIV_GLSL_XFB_USE_2ND_DERIVATIVES)
+ writeDuu(duu);
+ writeDuv(duv);
+ writeDvv(dvv);
+#endif
+}
+
+#endif
+
+
+
diff --git a/shaders/open-subdiv/3.shader_test b/shaders/open-subdiv/3.shader_test
new file mode 100644
index 0000000..cdd1dbf
--- /dev/null
+++ b/shaders/open-subdiv/3.shader_test
@@ -0,0 +1,29 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+in vec2 position;
+in vec3 color;
+in vec2 uv;
+out vec4 fragColor;
+out vec2 fragUV;
+uniform mat4 ModelViewProjectionMatrix;
+void main() {
+ fragColor = vec4(color, 1);
+ fragUV = uv;
+ gl_Position = ModelViewProjectionMatrix * vec4(position.x, position.y, 0, 1);
+}
+
+[fragment shader]
+#version 150
+in vec4 fragColor;
+in vec2 fragUV;
+out vec4 color;
+uniform sampler2D fontTexture;
+void main() {
+ vec4 c = texture(fontTexture, fragUV);
+ if (c.a == 0.0) discard;
+ color = c*fragColor;
+}
+
diff --git a/shaders/open-subdiv/6.shader_test b/shaders/open-subdiv/6.shader_test
new file mode 100644
index 0000000..128e690
--- /dev/null
+++ b/shaders/open-subdiv/6.shader_test
@@ -0,0 +1,22 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+out vec2 uv;
+void main() {
+ vec4 pos[4] = vec4[] (vec4(-1,-1,0,1), vec4(1,-1,0,1),
+ vec4(-1,1,0,1), vec4(1,1,0,1));
+ uv = pos[gl_VertexID].xy;
+ gl_Position = pos[gl_VertexID];
+}
+
+[fragment shader]
+#version 150
+in vec2 uv;
+out vec4 color;
+void main() {
+ color = vec4(mix(0.1, 0.5, sin((uv.y*0.5+0.5)*3.14159)));
+ color.a = 1.0;
+}
+
diff --git a/shaders/open-subdiv/7.shader_test b/shaders/open-subdiv/7.shader_test
new file mode 100644
index 0000000..a42a85f
--- /dev/null
+++ b/shaders/open-subdiv/7.shader_test
@@ -0,0 +1,25442 @@
+[require]
+GLSL >= 4.60
+
+[vertex shader]
+#version 460
+#define PRIM_TRI
+#define OSD_PATCH_ENABLE_SINGLE_CREASE
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_BSPLINE
+#define OSD_PATCH_VERTEX_BSPLINE_SHADER
+//
+// 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_BSPLINE_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_BSPLINE_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} outpt[16];
+
+layout(vertices = 16) out;
+
+void main()
+{
+ vec3 cv[16];
+ for (int i=0; i<16; ++i) {
+ cv[i] = inpt[i].v.position.xyz;
+ }
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexBSpline(patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Wait for all basis conversion to be finished
+ barrier();
+#endif
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(16);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier cpBezier[16];
+ cpBezier[0] = outpt[0].v;
+ cpBezier[1] = outpt[1].v;
+ cpBezier[2] = outpt[2].v;
+ cpBezier[3] = outpt[3].v;
+ cpBezier[4] = outpt[4].v;
+ cpBezier[5] = outpt[5].v;
+ cpBezier[6] = outpt[6].v;
+ cpBezier[7] = outpt[7].v;
+ cpBezier[8] = outpt[8].v;
+ cpBezier[9] = outpt[9].v;
+ cpBezier[10] = outpt[10].v;
+ cpBezier[11] = outpt[11].v;
+ cpBezier[12] = outpt[12].v;
+ cpBezier[13] = outpt[13].v;
+ cpBezier[14] = outpt[14].v;
+ cpBezier[15] = outpt[15].v;
+
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_BSPLINE_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ OsdPerPatchVertexBezier cv[16];
+ for (int i = 0; i < 16; ++i) {
+ cv[i] = inpt[i].v;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchBezier(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = cv[0].vSegments;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 5, 6, 9, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[tessellation control shader]
+#version 460
+#define PRIM_TRI
+#define OSD_PATCH_ENABLE_SINGLE_CREASE
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_BSPLINE
+#define OSD_PATCH_TESS_CONTROL_BSPLINE_SHADER
+//
+// 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_BSPLINE_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_BSPLINE_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} outpt[16];
+
+layout(vertices = 16) out;
+
+void main()
+{
+ vec3 cv[16];
+ for (int i=0; i<16; ++i) {
+ cv[i] = inpt[i].v.position.xyz;
+ }
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexBSpline(patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Wait for all basis conversion to be finished
+ barrier();
+#endif
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(16);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier cpBezier[16];
+ cpBezier[0] = outpt[0].v;
+ cpBezier[1] = outpt[1].v;
+ cpBezier[2] = outpt[2].v;
+ cpBezier[3] = outpt[3].v;
+ cpBezier[4] = outpt[4].v;
+ cpBezier[5] = outpt[5].v;
+ cpBezier[6] = outpt[6].v;
+ cpBezier[7] = outpt[7].v;
+ cpBezier[8] = outpt[8].v;
+ cpBezier[9] = outpt[9].v;
+ cpBezier[10] = outpt[10].v;
+ cpBezier[11] = outpt[11].v;
+ cpBezier[12] = outpt[12].v;
+ cpBezier[13] = outpt[13].v;
+ cpBezier[14] = outpt[14].v;
+ cpBezier[15] = outpt[15].v;
+
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_BSPLINE_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ OsdPerPatchVertexBezier cv[16];
+ for (int i = 0; i < 16; ++i) {
+ cv[i] = inpt[i].v;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchBezier(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = cv[0].vSegments;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 5, 6, 9, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[tessellation evaluation shader]
+#version 460
+#define PRIM_TRI
+#define OSD_PATCH_ENABLE_SINGLE_CREASE
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+#define OSD_PATCH_BSPLINE
+#define OSD_PATCH_TESS_EVAL_BSPLINE_SHADER
+//
+// 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.
+//
+
+//----------------------------------------------------------
+// Patches.VertexBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_VERTEX_BSPLINE_SHADER
+
+layout(location = 0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = position;
+ OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessControlBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_CONTROL_BSPLINE_SHADER
+
+patch out vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ ControlVertex v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} outpt[16];
+
+layout(vertices = 16) out;
+
+void main()
+{
+ vec3 cv[16];
+ for (int i=0; i<16; ++i) {
+ cv[i] = inpt[i].v.position.xyz;
+ }
+
+ ivec3 patchParam = OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID));
+ OsdComputePerPatchVertexBSpline(patchParam, gl_InvocationID, cv, outpt[gl_InvocationID].v);
+
+ OSD_USER_VARYING_PER_CONTROL_POINT(gl_InvocationID, gl_InvocationID);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Wait for all basis conversion to be finished
+ barrier();
+#endif
+ if (gl_InvocationID == 0) {
+ vec4 tessLevelOuter = vec4(0);
+ vec2 tessLevelInner = vec2(0);
+
+ OSD_PATCH_CULL(16);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ // Gather bezier control points to compute limit surface tess levels
+ OsdPerPatchVertexBezier cpBezier[16];
+ cpBezier[0] = outpt[0].v;
+ cpBezier[1] = outpt[1].v;
+ cpBezier[2] = outpt[2].v;
+ cpBezier[3] = outpt[3].v;
+ cpBezier[4] = outpt[4].v;
+ cpBezier[5] = outpt[5].v;
+ cpBezier[6] = outpt[6].v;
+ cpBezier[7] = outpt[7].v;
+ cpBezier[8] = outpt[8].v;
+ cpBezier[9] = outpt[9].v;
+ cpBezier[10] = outpt[10].v;
+ cpBezier[11] = outpt[11].v;
+ cpBezier[12] = outpt[12].v;
+ cpBezier[13] = outpt[13].v;
+ cpBezier[14] = outpt[14].v;
+ cpBezier[15] = outpt[15].v;
+
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessLevelOuter, tessLevelInner,
+ tessOuterLo, tessOuterHi);
+#endif
+
+ gl_TessLevelOuter[0] = tessLevelOuter[0];
+ gl_TessLevelOuter[1] = tessLevelOuter[1];
+ gl_TessLevelOuter[2] = tessLevelOuter[2];
+ gl_TessLevelOuter[3] = tessLevelOuter[3];
+
+ gl_TessLevelInner[0] = tessLevelInner[0];
+ gl_TessLevelInner[1] = tessLevelInner[1];
+ }
+}
+
+#endif
+
+//----------------------------------------------------------
+// Patches.TessEvalBSpline
+//----------------------------------------------------------
+#ifdef OSD_PATCH_TESS_EVAL_BSPLINE_SHADER
+
+layout(quads) in;
+layout(OSD_SPACING) in;
+
+patch in vec4 tessOuterLo, tessOuterHi;
+
+in block {
+ OsdPerPatchVertexBezier v;
+ OSD_USER_VARYING_DECLARE
+} inpt[];
+
+out block {
+ OutputVertex v;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ vec3 P = vec3(0), dPu = vec3(0), dPv = vec3(0);
+ vec3 N = vec3(0), dNu = vec3(0), dNv = vec3(0);
+
+ OsdPerPatchVertexBezier cv[16];
+ for (int i = 0; i < 16; ++i) {
+ cv[i] = inpt[i].v;
+ }
+
+ vec2 UV = OsdGetTessParameterization(gl_TessCoord.xy,
+ tessOuterLo,
+ tessOuterHi);
+
+ ivec3 patchParam = inpt[0].v.patchParam;
+ OsdEvalPatchBezier(patchParam, UV, cv, P, dPu, dPv, N, dNu, dNv);
+
+ // all code below here is client code
+ outpt.v.position = OsdModelViewMatrix() * vec4(P, 1.0f);
+ outpt.v.normal = (OsdModelViewMatrix() * vec4(N, 0.0f)).xyz;
+ outpt.v.tangent = (OsdModelViewMatrix() * vec4(dPu, 0.0f)).xyz;
+ outpt.v.bitangent = (OsdModelViewMatrix() * vec4(dPv, 0.0f)).xyz;
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ outpt.v.Nu = dNu;
+ outpt.v.Nv = dNv;
+#endif
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = cv[0].vSegments;
+#endif
+
+ outpt.v.tessCoord = UV;
+ outpt.v.patchCoord = OsdInterpolatePatchCoord(UV, patchParam);
+
+ OSD_USER_VARYING_PER_EVAL_POINT(UV, 5, 6, 9, 10);
+
+ OSD_DISPLACEMENT_CALLBACK;
+
+ gl_Position = OsdProjectionMatrix() * outpt.v.position;
+}
+
+#endif
+
+
+[geometry shader]
+#version 460
+#define PRIM_TRI
+#define OSD_PATCH_ENABLE_SINGLE_CREASE
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+#define GEOMETRY_SHADER
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+
+[fragment shader]
+#version 460
+#define PRIM_TRI
+#define OSD_PATCH_ENABLE_SINGLE_CREASE
+#define OSD_MAX_VALENCE 0
+#define OSD_NUM_ELEMENTS 0
+#define GEOMETRY_OUT_LINE
+#define SHADING_PATCH_TYPE
+#define SMOOTH_NORMALS
+#define OSD_PATCH_BASIS_GLSL
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_TYPES_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H
+
+#if defined(OSD_PATCH_BASIS_GLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) elementType[](a0,a1)
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) elementType[](a0,a1,a2)
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) elementType[](a0,a1,a2,a3)
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) elementType[](a0,a1,a2,a3,a4,a5)
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) elementType[](a0,a1,a2,a3,a4,a5,a6,a7)
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8)
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) elementType[](a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)
+
+#elif defined(OSD_PATCH_BASIS_HLSL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) out elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) inout elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_CUDA)
+
+ #define OSD_FUNCTION_STORAGE_CLASS __device__
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_OPENCL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS static
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST convert_float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#elif defined(OSD_PATCH_BASIS_METAL)
+
+ #define OSD_FUNCTION_STORAGE_CLASS
+ #define OSD_DATA_STORAGE_CLASS
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) true
+ #define OSD_OPTIONAL_INIT(a,b) b
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 0
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) thread elementType* identifier
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#else
+
+ #define OSD_FUNCTION_STORAGE_CLASS static inline
+ #define OSD_DATA_STORAGE_CLASS static
+ #define OSD_REAL float
+ #define OSD_REAL_CAST float
+ #define OSD_OPTIONAL(a) (a)
+ #define OSD_OPTIONAL_INIT(a,b) (a ? b : 0)
+ #define OSD_ARRAY_ARG_BOUND_OPTIONAL 1
+ #define OSD_IN_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_OUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_INOUT_ARRAY(elementType, identifier, arraySize) elementType identifier[arraySize]
+ #define OSD_ARRAY_2(elementType,a0,a1) {a0,a1}
+ #define OSD_ARRAY_3(elementType,a0,a1,a2) {a0,a1,a2}
+ #define OSD_ARRAY_4(elementType,a0,a1,a2,a3) {a0,a1,a2,a3}
+ #define OSD_ARRAY_6(elementType,a0,a1,a2,a3,a4,a5) {a0,a1,a2,a3,a4,a5}
+ #define OSD_ARRAY_8(elementType,a0,a1,a2,a3,a4,a5,a6,a7) {a0,a1,a2,a3,a4,a5,a6,a7}
+ #define OSD_ARRAY_9(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8) {a0,a1,a2,a3,a4,a5,a6,a7,a8}
+ #define OSD_ARRAY_12(elementType,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11}
+
+#endif
+
+#if defined(OSD_PATCH_BASIS_OPENCL)
+// OpenCL binding uses typedef to provide the required "struct" type specifier.
+typedef struct OsdPatchParam OsdPatchParam;
+typedef struct OsdPatchArray OsdPatchArray;
+typedef struct OsdPatchCoord OsdPatchCoord;
+#endif
+
+// Osd reflection of Far::PatchDescriptor
+#define OSD_PATCH_DESCRIPTOR_QUADS 3
+#define OSD_PATCH_DESCRIPTOR_TRIANGLES 4
+#define OSD_PATCH_DESCRIPTOR_LOOP 5
+#define OSD_PATCH_DESCRIPTOR_REGULAR 6
+#define OSD_PATCH_DESCRIPTOR_GREGORY_BASIS 9
+#define OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE 10
+
+// Osd reflection of Osd::PatchCoord
+struct OsdPatchCoord {
+ int arrayIndex;
+ int patchIndex;
+ int vertIndex;
+ float s;
+ float t;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchCoord
+OsdPatchCoordInit(
+ int arrayIndex, int patchIndex, int vertIndex, float s, float t)
+{
+ OsdPatchCoord coord;
+ coord.arrayIndex = arrayIndex;
+ coord.patchIndex = patchIndex;
+ coord.vertIndex = vertIndex;
+ coord.s = s;
+ coord.t = t;
+ return coord;
+}
+
+// Osd reflection of Osd::PatchArray
+struct OsdPatchArray {
+ int regDesc;
+ int desc;
+ int numPatches;
+ int indexBase;
+ int stride;
+ int primitiveIdBase;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchArray
+OsdPatchArrayInit(
+ int regDesc, int desc,
+ int numPatches, int indexBase, int stride, int primitiveIdBase)
+{
+ OsdPatchArray array;
+ array.regDesc = regDesc;
+ array.desc = desc;
+ array.numPatches = numPatches;
+ array.indexBase = indexBase;
+ array.stride = stride;
+ array.primitiveIdBase = primitiveIdBase;
+ return array;
+}
+
+// Osd reflection of Osd::PatchParam
+struct OsdPatchParam {
+ int field0;
+ int field1;
+ float sharpness;
+};
+
+OSD_FUNCTION_STORAGE_CLASS
+OsdPatchParam
+OsdPatchParamInit(int field0, int field1, float sharpness)
+{
+ OsdPatchParam param;
+ param.field0 = field0;
+ param.field1 = field1;
+ param.sharpness = sharpness;
+ return param;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetFaceId(OsdPatchParam param)
+{
+ return (param.field0 & 0xfffffff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetU(OsdPatchParam param)
+{
+ return ((param.field1 >> 22) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetV(OsdPatchParam param)
+{
+ return ((param.field1 >> 12) & 0x3ff);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetTransition(OsdPatchParam param)
+{
+ return ((param.field0 >> 28) & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetBoundary(OsdPatchParam param)
+{
+ return ((param.field1 >> 7) & 0x1f);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetNonQuadRoot(OsdPatchParam param)
+{
+ return ((param.field1 >> 4) & 0x1);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+OsdPatchParamGetDepth(OsdPatchParam param)
+{
+ return (param.field1 & 0xf);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+OSD_REAL
+OsdPatchParamGetParamFraction(OsdPatchParam param)
+{
+ return 1.0f / OSD_REAL_CAST(1 <<
+ (OsdPatchParamGetDepth(param) - OsdPatchParamGetNonQuadRoot(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsRegular(OsdPatchParam param)
+{
+ return (((param.field1 >> 5) & 0x1) != 0);
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+bool
+OsdPatchParamIsTriangleRotated(OsdPatchParam param)
+{
+ return ((OsdPatchParamGetU(param) + OsdPatchParamGetV(param)) >=
+ (1 << OsdPatchParamGetDepth(param)));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ uv[0] = uv[0] * fracInv - OSD_REAL_CAST(OsdPatchParamGetU(param));
+ uv[1] = uv[1] * fracInv - OSD_REAL_CAST(OsdPatchParamGetV(param));
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalize(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ uv[0] = (uv[0] + OSD_REAL_CAST(OsdPatchParamGetU(param))) * frac;
+ uv[1] = (uv[1] + OSD_REAL_CAST(OsdPatchParamGetV(param))) * frac;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamNormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL fracInv = 1.0f / OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - (uv[0] * fracInv);
+ uv[1] = OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - (uv[1] * fracInv);
+ } else {
+ OsdPatchParamNormalize(param, uv);
+ }
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdPatchParamUnnormalizeTriangle(
+ OsdPatchParam param,
+ OSD_INOUT_ARRAY(OSD_REAL, uv, 2))
+{
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ OSD_REAL frac = OsdPatchParamGetParamFraction(param);
+
+ int depthFactor = 1 << OsdPatchParamGetDepth(param);
+ uv[0] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetU(param)) - uv[0]) * frac;
+ uv[1] = (OSD_REAL_CAST(depthFactor - OsdPatchParamGetV(param)) - uv[1]) * frac;
+ } else {
+ OsdPatchParamUnnormalize(param, uv);
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_TYPES_H */
+
+//
+// Copyright 2016-2018 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_OSD_PATCH_BASIS_COMMON_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinear(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = sC * tC;
+ wP[1] = s * tC;
+ wP[2] = s * t;
+ wP[3] = sC * t;
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -tC;
+ wDs[1] = tC;
+ wDs[2] = t;
+ wDs[3] = -t;
+
+ wDt[0] = -sC;
+ wDt[1] = -s;
+ wDt[2] = s;
+ wDt[3] = sC;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for(int i=0;i<4;i++) {
+ wDss[i] = 0.0f;
+ wDtt[i] = 0.0f;
+ }
+
+ wDst[0] = 1.0f;
+ wDst[1] = -1.0f;
+ wDst[2] = 1.0f;
+ wDst[3] = -1.0f;
+ }
+ }
+ return 4;
+}
+
+// namespace {
+ //
+ // Cubic BSpline curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBSplineCurve(OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ const OSD_REAL one6th = OSD_REAL_CAST(1.0f / 6.0f);
+
+ OSD_REAL t2 = t * t;
+ OSD_REAL t3 = t * t2;
+
+ wP[0] = one6th * (1.0f - 3.0f*(t - t2) - t3);
+ wP[1] = one6th * (4.0f - 6.0f*t2 + 3.0f*t3);
+ wP[2] = one6th * (1.0f + 3.0f*(t + t2 - t3));
+ wP[3] = one6th * ( t3);
+
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -0.5f*t2 + t - 0.5f;
+ wDP[1] = 1.5f*t2 - 2.0f*t;
+ wDP[2] = -1.5f*t2 + t + 0.5f;
+ wDP[3] = 0.5f*t2;
+ }
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = - t + 1.0f;
+ wDP2[1] = 3.0f * t - 2.0f;
+ wDP2[2] = -3.0f * t + 1.0f;
+ wDP2[3] = t;
+ }
+ }
+
+ //
+ // Weight adjustments to account for phantom end points:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBSplineBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, w, 16)) {
+
+ if ((boundary & 1) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 8] -= w[i + 0];
+ w[i + 4] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ if ((boundary & 2) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 1] -= w[i + 3];
+ w[i + 2] += w[i + 3] * 2.0f;
+ w[i + 3] = 0.0f;
+ }
+ }
+ if ((boundary & 4) != 0) {
+ for (int i = 0; i < 4; ++i) {
+ w[i + 4] -= w[i + 12];
+ w[i + 8] += w[i + 12] * 2.0f;
+ w[i + 12] = 0.0f;
+ }
+ }
+ if ((boundary & 8) != 0) {
+ for (int i = 0; i < 16; i += 4) {
+ w[i + 2] -= w[i + 0];
+ w[i + 1] += w[i + 0] * 2.0f;
+ w[i + 0] = 0.0f;
+ }
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBSpline(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDs);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBSplineBoundaryWeights(boundary, wDss);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDst);
+ Osd_adjustBSplineBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBSpline(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBSplineCurve(s, sWeights, OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBSplineCurve(t, tWeights, OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+// namespace {
+ //
+ // Cubic Bezier curve basis evaluation:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierCurve(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ // The four uniform cubic Bezier basis functions (in terms of t and its
+ // complement tC) evaluated at t:
+ OSD_REAL t2 = t*t;
+ OSD_REAL tC = 1.0f - t;
+ OSD_REAL tC2 = tC * tC;
+
+ wP[0] = tC2 * tC;
+ wP[1] = tC2 * t * 3.0f;
+ wP[2] = t2 * tC * 3.0f;
+ wP[3] = t2 * t;
+
+ // Derivatives of the above four basis functions at t:
+ if (OSD_OPTIONAL(wDP)) {
+ wDP[0] = -3.0f * tC2;
+ wDP[1] = 9.0f * t2 - 12.0f * t + 3.0f;
+ wDP[2] = -9.0f * t2 + 6.0f * t;
+ wDP[3] = 3.0f * t2;
+ }
+
+ // Second derivatives of the basis functions at t:
+ if (OSD_OPTIONAL(wDP2)) {
+ wDP2[0] = 6.0f * tC;
+ wDP2[1] = 18.0f * t - 12.0f;
+ wDP2[2] = -18.0f * t + 6.0f;
+ wDP2[3] = 6.0f * t;
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisBezier(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ OSD_REAL sWeights[4], tWeights[4], dsWeights[4], dtWeights[4], dssWeights[4], dttWeights[4];
+
+ Osd_evalBezierCurve(s, OSD_OPTIONAL_INIT(wP, sWeights), OSD_OPTIONAL_INIT(wDs, dsWeights), OSD_OPTIONAL_INIT(wDss, dssWeights));
+ Osd_evalBezierCurve(t, OSD_OPTIONAL_INIT(wP, tWeights), OSD_OPTIONAL_INIT(wDt, dtWeights), OSD_OPTIONAL_INIT(wDtt, dttWeights));
+
+ if (OSD_OPTIONAL(wP)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i];
+ wDt[4*i+j] = sWeights[j] * dtWeights[i];
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i];
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i];
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i];
+ }
+ }
+ }
+ }
+ return 16;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+int
+Osd_EvalBasisGregory(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ // Indices of boundary and interior points and their corresponding Bezier points
+ // (this can be reduced with more direct indexing and unrolling of loops):
+ //
+ OSD_DATA_STORAGE_CLASS const int boundaryGregory[12] = OSD_ARRAY_12(int, 0, 1, 7, 5, 2, 6, 16, 12, 15, 17, 11, 10 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezSCol[12] = OSD_ARRAY_12(int, 0, 1, 2, 3, 0, 3, 0, 3, 0, 1, 2, 3 );
+ OSD_DATA_STORAGE_CLASS const int boundaryBezTRow[12] = OSD_ARRAY_12(int, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 );
+
+ OSD_DATA_STORAGE_CLASS const int interiorGregory[8] = OSD_ARRAY_8(int, 3, 4, 8, 9, 13, 14, 18, 19 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezSCol[8] = OSD_ARRAY_8(int, 1, 1, 2, 2, 2, 2, 1, 1 );
+ OSD_DATA_STORAGE_CLASS const int interiorBezTRow[8] = OSD_ARRAY_8(int, 1, 1, 1, 1, 2, 2, 2, 2 );
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s), B(t) and G(s,t):
+ //
+ // Directional Bezier basis functions B at s and t:
+ OSD_REAL Bs[4], Bds[4], Bdss[4];
+ OSD_REAL Bt[4], Bdt[4], Bdtt[4];
+
+ Osd_evalBezierCurve(s, Bs, OSD_OPTIONAL_INIT(wDs, Bds), OSD_OPTIONAL_INIT(wDss, Bdss));
+ Osd_evalBezierCurve(t, Bt, OSD_OPTIONAL_INIT(wDt, Bdt), OSD_OPTIONAL_INIT(wDtt, Bdtt));
+
+ // Rational multipliers G at s and t:
+ OSD_REAL sC = 1.0f - s;
+ OSD_REAL tC = 1.0f - t;
+
+ // Use <= here to avoid compiler warnings -- the sums should always be non-negative:
+ OSD_REAL df0 = s + t; df0 = (df0 <= 0.0f) ? 1.0f : (1.0f / df0);
+ OSD_REAL df1 = sC + t; df1 = (df1 <= 0.0f) ? 1.0f : (1.0f / df1);
+ OSD_REAL df2 = sC + tC; df2 = (df2 <= 0.0f) ? 1.0f : (1.0f / df2);
+ OSD_REAL df3 = s + tC; df3 = (df3 <= 0.0f) ? 1.0f : (1.0f / df3);
+
+ // Make sure the G[i] for pairs of interior points sum to 1 in all cases:
+ OSD_REAL G[8] = OSD_ARRAY_8(OSD_REAL, s*df0, (1.0f - s*df0),
+ t*df1, (1.0f - t*df1),
+ sC*df2, (1.0f - sC*df2),
+ tC*df3, (1.0f - tC*df3) );
+
+ // Combined weights for boundary and interior points:
+ for (int i = 0; i < 12; ++i) {
+ wP[boundaryGregory[i]] = Bs[boundaryBezSCol[i]] * Bt[boundaryBezTRow[i]];
+ }
+ for (int j = 0; j < 8; ++j) {
+ wP[interiorGregory[j]] = Bs[interiorBezSCol[j]] * Bt[interiorBezTRow[j]] * G[j];
+ }
+
+ //
+ // For derivatives, the basis functions for the interior points are rational and ideally
+ // require appropriate differentiation, i.e. product rule for the combination of B and G
+ // and the quotient rule for the rational G itself. As initially proposed by Loop et al
+ // though, the approximation using the 16 Bezier points arising from the G(s,t) has
+ // proved adequate (and is what the GPU shaders use) so we continue to use that here.
+ //
+ // An implementation of the true derivatives is provided and conditionally compiled for
+ // those that require it, e.g.:
+ //
+ // dclyde's note: skipping half of the product rule like this does seem to change the
+ // result a lot in my tests. This is not a runtime bottleneck for cloth sims anyway
+ // so I'm just using the accurate version.
+ //
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ bool find_second_partials = OSD_OPTIONAL(wDs && wDst && wDtt);
+
+ // Combined weights for boundary points -- simple tensor products:
+ for (int i = 0; i < 12; ++i) {
+ int iDst = boundaryGregory[i];
+ int tRow = boundaryBezTRow[i];
+ int sCol = boundaryBezSCol[i];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow];
+ }
+ }
+
+#ifndef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+ // Approximation to the true Gregory derivatives by differentiating the Bezier patch
+ // unique to the given (s,t), i.e. having F = (g^+ * f^+) + (g^- * f^-) as its four
+ // interior points:
+ //
+ // Combined weights for interior points -- tensor products with G+ or G-:
+ for (int j = 0; j < 8; ++j) {
+ int iDst = interiorGregory[j];
+ int tRow = interiorBezTRow[j];
+ int sCol = interiorBezSCol[j];
+
+ wDs[iDst] = Bds[sCol] * Bt[tRow] * G[j];
+ wDt[iDst] = Bdt[tRow] * Bs[sCol] * G[j];
+
+ if (find_second_partials) {
+ wDss[iDst] = Bdss[sCol] * Bt[tRow] * G[j];
+ wDst[iDst] = Bds[sCol] * Bdt[tRow] * G[j];
+ wDtt[iDst] = Bs[sCol] * Bdtt[tRow] * G[j];
+ }
+ }
+#else
+ // True Gregory derivatives using appropriate differentiation of composite functions:
+ //
+ // Note that for G(s,t) = N(s,t) / D(s,t), all N' and D' are trivial constants (which
+ // simplifies things for higher order derivatives). And while each pair of functions
+ // G (i.e. the G+ and G- corresponding to points f+ and f-) must sum to 1 to ensure
+ // Bezier equivalence (when f+ = f-), the pairs of G' must similarly sum to 0. So we
+ // can potentially compute only one of the pair and negate the result for the other
+ // (and with 4 or 8 computations involving these constants, this is all very SIMD
+ // friendly...) but for now we treat all 8 independently for simplicity.
+ //
+ //float N[8] = OSD_ARRAY_8(float, s, t, t, sC, sC, tC, tC, s );
+ OSD_REAL D[8] = OSD_ARRAY_8(OSD_REAL, df0, df0, df1, df1, df2, df2, df3, df3 );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Nds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ndt[8] = OSD_ARRAY_8(OSD_REAL, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f );
+
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Dds[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f );
+ OSD_DATA_STORAGE_CLASS const OSD_REAL Ddt[8] = OSD_ARRAY_8(OSD_REAL, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f );
+ // Combined weights for interior points -- (scaled) combinations of B, B', G and G':
+ for (int k = 0; k < 8; ++k) {
+ int iDst = interiorGregory[k];
+ int tRow = interiorBezTRow[k];
+ int sCol = interiorBezSCol[k];
+
+ // Quotient rule for G' (re-expressed in terms of G to simplify (and D = 1/D)):
+ OSD_REAL Gds = (Nds[k] - Dds[k] * G[k]) * D[k];
+ OSD_REAL Gdt = (Ndt[k] - Ddt[k] * G[k]) * D[k];
+
+ // Product rule combining B and B' with G and G':
+ wDs[iDst] = (Bds[sCol] * G[k] + Bs[sCol] * Gds) * Bt[tRow];
+ wDt[iDst] = (Bdt[tRow] * G[k] + Bt[tRow] * Gdt) * Bs[sCol];
+
+ if (find_second_partials) {
+ OSD_REAL Dsqr_inv = D[k]*D[k];
+
+ OSD_REAL Gdss = 2.0f * Dds[k] * Dsqr_inv * (G[k] * Dds[k] - Nds[k]);
+ OSD_REAL Gdst = Dsqr_inv * (2.0f * G[k] * Dds[k] * Ddt[k] - Nds[k] * Ddt[k] - Ndt[k] * Dds[k]);
+ OSD_REAL Gdtt = 2.0f * Ddt[k] * Dsqr_inv * (G[k] * Ddt[k] - Ndt[k]);
+
+ wDss[iDst] = (Bdss[sCol] * G[k] + 2.0f * Bds[sCol] * Gds + Bs[sCol] * Gdss) * Bt[tRow];
+ wDst[iDst] = Bt[tRow] * (Bs[sCol] * Gdst + Bds[sCol] * Gdt) +
+ Bdt[tRow] * (Bds[sCol] * G[k] + Bs[sCol] * Gds);
+ wDtt[iDst] = (Bdtt[tRow] * G[k] + 2.0f * Bdt[tRow] * Gdt + Bt[tRow] * Gdtt) * Bs[sCol];
+ }
+ }
+#endif
+ }
+ return 20;
+}
+
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisLinearTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 3),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 3)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ wP[0] = 1.0f - s - t;
+ wP[1] = s;
+ wP[2] = t;
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ wDs[0] = -1.0f;
+ wDs[1] = 1.0f;
+ wDs[2] = 0.0f;
+
+ wDt[0] = -1.0f;
+ wDt[1] = 0.0f;
+ wDt[2] = 1.0f;
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ wDss[0] = wDss[1] = wDss[2] = 0.0f;
+ wDst[0] = wDst[1] = wDst[2] = 0.0f;
+ wDtt[0] = wDtt[1] = wDtt[2] = 0.0f;
+ }
+ }
+ return 3;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBivariateMonomialsQuartic(
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, M, 15)) {
+
+ M[0] = 1.0;
+
+ M[1] = s;
+ M[2] = t;
+
+ M[3] = s * s;
+ M[4] = s * t;
+ M[5] = t * t;
+
+ M[6] = M[3] * s;
+ M[7] = M[4] * s;
+ M[8] = M[4] * t;
+ M[9] = M[5] * t;
+
+ M[10] = M[6] * s;
+ M[11] = M[7] * s;
+ M[12] = M[3] * M[5];
+ M[13] = M[8] * t;
+ M[14] = M[9] * t;
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBoxSplineTriDerivWeights(
+ OSD_INOUT_ARRAY(OSD_REAL, /*stMonomials*/M, 15),
+ int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, w, 12)) {
+
+ // const OSD_REAL M[15] = stMonomials;
+
+ OSD_REAL S = 1.0f;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ S *= OSD_REAL_CAST(1.0f / 12.0f);
+
+ w[0] = S * (1 - 2*M[1] - 4*M[2] + 6*M[4] + 6*M[5] + 2*M[6] - 6*M[8] - 4*M[9] - M[10] - 2*M[11] + 2*M[13] + M[14]);
+ w[1] = S * (1 + 2*M[1] - 2*M[2] - 6*M[4] - 4*M[6] + 6*M[8] + 2*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[2] = S * ( 2*M[6] - M[10] - 2*M[11] );
+ w[3] = S * (1 - 4*M[1] - 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] + 2*M[9] + M[10] + 2*M[11] - 2*M[13] - M[14]);
+ w[4] = S * (6 -12*M[3] -12*M[4] -12*M[5] + 8*M[6] +12*M[7] +12*M[8] + 8*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[5] = S * (1 + 4*M[1] + 2*M[2] + 6*M[3] + 6*M[4] - 4*M[6] - 6*M[7] -12*M[8] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[6] = S * ( M[10] + 2*M[11] );
+ w[7] = S * (1 - 2*M[1] + 2*M[2] - 6*M[4] + 2*M[6] + 6*M[7] - 4*M[9] - M[10] - 2*M[11] + 4*M[13] + 2*M[14]);
+ w[8] = S * (1 + 2*M[1] + 4*M[2] + 6*M[4] + 6*M[5] - 4*M[6] -12*M[7] - 6*M[8] - 4*M[9] + 2*M[10] + 4*M[11] - 2*M[13] - M[14]);
+ w[9] = S * ( 2*M[6] + 6*M[7] + 6*M[8] + 2*M[9] - M[10] - 2*M[11] - 2*M[13] - M[14]);
+ w[10] = S * ( 2*M[9] - 2*M[13] - M[14]);
+ w[11] = S * ( 2*M[13] + M[14]);
+ } else if (totalOrder == 1) {
+ S *= OSD_REAL_CAST(1.0f / 6.0f);
+
+ if (ds != 0) {
+ w[0] = S * (-1 + 3*M[2] + 3*M[3] - 3*M[5] - 2*M[6] - 3*M[7] + M[9]);
+ w[1] = S * ( 1 - 3*M[2] - 6*M[3] + 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[2] = S * ( 3*M[3] - 2*M[6] - 3*M[7] );
+ w[3] = S * (-2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] + 2*M[6] + 3*M[7] - M[9]);
+ w[4] = S * ( -12*M[1] - 6*M[2] +12*M[3] +12*M[4] + 6*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[5] = S * ( 2 + 6*M[1] + 3*M[2] - 6*M[3] - 6*M[4] - 6*M[5] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[6] = S * ( 2*M[6] + 3*M[7] );
+ w[7] = S * (-1 - 3*M[2] + 3*M[3] + 6*M[4] - 2*M[6] - 3*M[7] + 2*M[9]);
+ w[8] = S * ( 1 + 3*M[2] - 6*M[3] -12*M[4] - 3*M[5] + 4*M[6] + 6*M[7] - M[9]);
+ w[9] = S * ( 3*M[3] + 6*M[4] + 3*M[5] - 2*M[6] - 3*M[7] - M[9]);
+ w[10] = S * ( - M[9]);
+ w[11] = S * ( M[9]);
+ } else {
+ w[0] = S * (-2 + 3*M[1] + 6*M[2] - 6*M[4] - 6*M[5] - M[6] + 3*M[8] + 2*M[9]);
+ w[1] = S * (-1 - 3*M[1] + 6*M[4] + 3*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[2] = S * ( - M[6] );
+ w[3] = S * (-1 + 3*M[1] - 3*M[3] + 3*M[5] + M[6] - 3*M[8] - 2*M[9]);
+ w[4] = S * ( - 6*M[1] -12*M[2] + 6*M[3] +12*M[4] +12*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[5] = S * ( 1 + 3*M[1] - 3*M[3] -12*M[4] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[6] = S * ( + M[6] );
+ w[7] = S * ( 1 - 3*M[1] + 3*M[3] - 6*M[5] - M[6] + 6*M[8] + 4*M[9]);
+ w[8] = S * ( 2 + 3*M[1] + 6*M[2] - 6*M[3] - 6*M[4] - 6*M[5] + 2*M[6] - 3*M[8] - 2*M[9]);
+ w[9] = S * ( + 3*M[3] + 6*M[4] + 3*M[5] - M[6] - 3*M[8] - 2*M[9]);
+ w[10] = S * ( 3*M[5] - 3*M[8] - 2*M[9]);
+ w[11] = S * ( 3*M[8] + 2*M[9]);
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ w[0] = S * ( + M[1] - M[3] - M[4]);
+ w[1] = S * ( - 2*M[1] + 2*M[3] + 2*M[4]);
+ w[2] = S * ( M[1] - M[3] - M[4]);
+ w[3] = S * ( 1 - 2*M[1] - M[2] + M[3] + M[4]);
+ w[4] = S * (-2 + 4*M[1] + 2*M[2] - M[3] - M[4]);
+ w[5] = S * ( 1 - 2*M[1] - M[2] - M[3] - M[4]);
+ w[6] = S * ( M[3] + M[4]);
+ w[7] = S * ( + M[1] + M[2] - M[3] - M[4]);
+ w[8] = S * ( - 2*M[1] - 2*M[2] + 2*M[3] + 2*M[4]);
+ w[9] = S * ( M[1] + M[2] - M[3] - M[4]);
+ w[10] = 0;
+ w[11] = 0;
+ } else if (dt == 2) {
+ w[0] = S * ( 1 - M[1] - 2*M[2] + M[4] + M[5]);
+ w[1] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[2] = 0;
+ w[3] = S * ( + M[2] - M[4] - M[5]);
+ w[4] = S * (-2 + 2*M[1] + 4*M[2] - M[4] - M[5]);
+ w[5] = S * ( - 2*M[1] - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[6] = 0;
+ w[7] = S * ( - 2*M[2] + 2*M[4] + 2*M[5]);
+ w[8] = S * ( 1 - M[1] - 2*M[2] - M[4] - M[5]);
+ w[9] = S * ( + M[1] + M[2] - M[4] - M[5]);
+ w[10] = S * ( M[2] - M[4] - M[5]);
+ w[11] = S * ( M[4] + M[5]);
+ } else {
+ S *= OSD_REAL_CAST(1.0f / 2.0f);
+
+ w[0] = S * ( 1 - 2*M[2] - M[3] + M[5]);
+ w[1] = S * (-1 + 2*M[2] + 2*M[3] - M[5]);
+ w[2] = S * ( - M[3] );
+ w[3] = S * ( 1 - 2*M[1] + M[3] - M[5]);
+ w[4] = S * (-2 + 4*M[1] + 4*M[2] - M[3] - M[5]);
+ w[5] = S * ( 1 - 2*M[1] - 4*M[2] - M[3] + 2*M[5]);
+ w[6] = S * ( + M[3] );
+ w[7] = S * (-1 + 2*M[1] - M[3] + 2*M[5]);
+ w[8] = S * ( 1 - 4*M[1] - 2*M[2] + 2*M[3] - M[5]);
+ w[9] = S * ( + 2*M[1] + 2*M[2] - M[3] - M[5]);
+ w[10] = S * ( - M[5]);
+ w[11] = S * ( M[5]);
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_adjustBoxSplineTriBoundaryWeights(
+ int boundaryMask,
+ OSD_INOUT_ARRAY(OSD_REAL, weights, 12)) {
+
+ if (boundaryMask == 0) return;
+
+ //
+ // Determine boundary edges and vertices from the lower 3 and upper
+ // 2 bits of the 5-bit mask:
+ //
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ // Boundary vertices only:
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ //
+ // Adjust weights for the 4 boundary points and 3 interior points
+ // to account for the 3 phantom points adjacent to each
+ // boundary edge:
+ //
+ if (edge0IsBoundary) {
+ OSD_REAL w0 = weights[0];
+ if (edge2IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[4] += w0;
+ weights[4] += w0;
+ weights[8] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[4] += w0;
+ weights[3] += w0;
+ weights[7] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[1];
+ weights[4] += w1;
+ weights[5] += w1;
+ weights[8] -= w1;
+
+ OSD_REAL w2 = weights[2];
+ if (edge1IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[5] += w2;
+ weights[5] += w2;
+ weights[8] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[5] += w2;
+ weights[6] += w2;
+ weights[9] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[0] = weights[1] = weights[2] = 0.0f;
+ }
+ if (edge1IsBoundary) {
+ OSD_REAL w0 = weights[6];
+ if (edge0IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[5] += w0;
+ weights[5] += w0;
+ weights[4] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[5] += w0;
+ weights[2] += w0;
+ weights[1] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[9];
+ weights[5] += w1;
+ weights[8] += w1;
+ weights[4] -= w1;
+
+ OSD_REAL w2 = weights[11];
+ if (edge2IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[8] += w2;
+ weights[8] += w2;
+ weights[4] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[8] += w2;
+ weights[10] += w2;
+ weights[7] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[6] = weights[9] = weights[11] = 0.0f;
+ }
+ if (edge2IsBoundary) {
+ OSD_REAL w0 = weights[10];
+ if (edge1IsBoundary) {
+ // P0 = B1 + (B1 - I1)
+ weights[8] += w0;
+ weights[8] += w0;
+ weights[5] -= w0;
+ } else {
+ // P0 = B1 + (B0 - I0)
+ weights[8] += w0;
+ weights[11] += w0;
+ weights[9] -= w0;
+ }
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[7];
+ weights[8] += w1;
+ weights[4] += w1;
+ weights[5] -= w1;
+
+ OSD_REAL w2 = weights[3];
+ if (edge0IsBoundary) {
+ // P2 = B2 + (B2 - I1)
+ weights[4] += w2;
+ weights[4] += w2;
+ weights[5] -= w2;
+ } else {
+ // P2 = B2 + (B3 - I2)
+ weights[4] += w2;
+ weights[0] += w2;
+ weights[1] -= w2;
+ }
+ // Clear weights for the phantom points:
+ weights[10] = weights[7] = weights[3] = 0.0f;
+ }
+
+ //
+ // Adjust weights for the 3 boundary points and the 2 interior
+ // points to account for the 2 phantom points adjacent to
+ // each boundary vertex:
+ //
+ if ((vBits & 1) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[3];
+ weights[4] += w0;
+ weights[7] += w0;
+ weights[8] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[0];
+ weights[4] += w1;
+ weights[1] += w1;
+ weights[5] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[3] = weights[0] = 0.0f;
+ }
+ if ((vBits & 2) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[2];
+ weights[5] += w0;
+ weights[1] += w0;
+ weights[4] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[6];
+ weights[5] += w1;
+ weights[9] += w1;
+ weights[8] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[2] = weights[6] = 0.0f;
+ }
+ if ((vBits & 4) != 0) {
+ // P0 = B1 + (B0 - I0)
+ OSD_REAL w0 = weights[11];
+ weights[8] += w0;
+ weights[9] += w0;
+ weights[5] -= w0;
+
+ // P1 = B1 + (B2 - I1)
+ OSD_REAL w1 = weights[10];
+ weights[8] += w1;
+ weights[7] += w1;
+ weights[4] -= w1;
+
+ // Clear weights for the phantom points:
+ weights[11] = weights[10] = 0.0f;
+ }
+ }
+
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_boundBasisBoxSplineTri(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_INOUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDs);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDss);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDst);
+ Osd_adjustBoxSplineTriBoundaryWeights(boundary, wDtt);
+ }
+ }
+ }
+// } // namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBoxSplineTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 12),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 0, wDs);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 2, 0, wDss);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 1, 1, wDst);
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 2, wDtt);
+ }
+ }
+ return 12;
+}
+
+
+// namespace {
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_evalBezierTriDerivWeights(
+ OSD_REAL s, OSD_REAL t, int ds, int dt,
+ OSD_OUT_ARRAY(OSD_REAL, wB, 15)) {
+
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ OSD_REAL uu = u * u;
+ OSD_REAL vv = v * v;
+ OSD_REAL ww = w * w;
+
+ OSD_REAL uv = u * v;
+ OSD_REAL vw = v * w;
+ OSD_REAL uw = u * w;
+
+ int totalOrder = ds + dt;
+ if (totalOrder == 0) {
+ wB[0] = ww * ww;
+ wB[1] = 4 * uw * ww;
+ wB[2] = 6 * uw * uw;
+ wB[3] = 4 * uw * uu;
+ wB[4] = uu * uu;
+ wB[5] = 4 * vw * ww;
+ wB[6] = 12 * ww * uv;
+ wB[7] = 12 * uu * vw;
+ wB[8] = 4 * uv * uu;
+ wB[9] = 6 * vw * vw;
+ wB[10] = 12 * vv * uw;
+ wB[11] = 6 * uv * uv;
+ wB[12] = 4 * vw * vv;
+ wB[13] = 4 * uv * vv;
+ wB[14] = vv * vv;
+ } else if (totalOrder == 1) {
+ if (ds == 1) {
+ wB[0] = -4 * ww * w;
+ wB[1] = 4 * ww * (w - 3 * u);
+ wB[2] = 12 * uw * (w - u);
+ wB[3] = 4 * uu * (3 * w - u);
+ wB[4] = 4 * uu * u;
+ wB[5] = -12 * vw * w;
+ wB[6] = 12 * vw * (w - 2 * u);
+ wB[7] = 12 * uv * (2 * w - u);
+ wB[8] = 12 * uv * u;
+ wB[9] = -12 * vv * w;
+ wB[10] = 12 * vv * (w - u);
+ wB[11] = 12 * vv * u;
+ wB[12] = -4 * vv * v;
+ wB[13] = 4 * vv * v;
+ wB[14] = 0;
+ } else {
+ wB[0] = -4 * ww * w;
+ wB[1] = -12 * ww * u;
+ wB[2] = -12 * uu * w;
+ wB[3] = -4 * uu * u;
+ wB[4] = 0;
+ wB[5] = 4 * ww * (w - 3 * v);
+ wB[6] = 12 * uw * (w - 2 * v);
+ wB[7] = 12 * uu * (w - v);
+ wB[8] = 4 * uu * u;
+ wB[9] = 12 * vw * (w - v);
+ wB[10] = 12 * uv * (2 * w - v);
+ wB[11] = 12 * uv * u;;
+ wB[12] = 4 * vv * (3 * w - v);
+ wB[13] = 12 * vv * u;
+ wB[14] = 4 * vv * v;
+ }
+ } else if (totalOrder == 2) {
+ if (ds == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * (uw - ww);
+ wB[2] = 12 * (uu - 4 * uw + ww);
+ wB[3] = 24 * (uw - uu);
+ wB[4] = 12 * uu;
+ wB[5] = 24 * vw;
+ wB[6] = 24 * (uv - 2 * vw);
+ wB[7] = 24 * (vw - 2 * uv);
+ wB[8] = 24 * uv;
+ wB[9] = 12 * vv;
+ wB[10] = -24 * vv;
+ wB[11] = 12 * vv;
+ wB[12] = 0;
+ wB[13] = 0;
+ wB[14] = 0;
+ } else if (dt == 2) {
+ wB[0] = 12 * ww;
+ wB[1] = 24 * uw;
+ wB[2] = 12 * uu;
+ wB[3] = 0;
+ wB[4] = 0;
+ wB[5] = 24 * (vw - ww);
+ wB[6] = 24 * (uv - 2 * uw);
+ wB[7] = -24 * uu;
+ wB[8] = 0;
+ wB[9] = 12 * (vv - 4 * vw + ww);
+ wB[10] = 24 * (uw - 2 * uv);
+ wB[11] = 12 * uu;
+ wB[12] = 24 * (vw - vv);
+ wB[13] = 24 * uv;
+ wB[14] = 12 * vv;
+ } else {
+ wB[0] = 12 * ww;
+ wB[3] = -12 * uu;
+ wB[13] = 12 * vv;
+ wB[11] = 24 * uv;
+ wB[1] = 24 * uw - wB[0];
+ wB[2] = -24 * uw - wB[3];
+ wB[5] = 24 * vw - wB[0];
+ wB[6] = -24 * vw + wB[11] - wB[1];
+ wB[8] = - wB[3];
+ wB[7] = -(wB[11] + wB[2]);
+ wB[9] = wB[13] - wB[5] - wB[0];
+ wB[10] = -(wB[9] + wB[11]);
+ wB[12] = - wB[13];
+ wB[4] = 0;
+ wB[14] = 0;
+ }
+ } else {
+ // assert(totalOrder <= 2);
+ }
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisBezierTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 15),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 15)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, wDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, wDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, wDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, wDtt);
+ }
+ }
+ return 15;
+}
+
+
+// namespace {
+ //
+ // Expanding a set of 15 Bezier basis functions for the 6 (3 pairs) of
+ // rational weights for the 18 Gregory basis functions:
+ //
+ OSD_FUNCTION_STORAGE_CLASS
+ // template <typename REAL>
+ void
+ Osd_convertBezierWeightsToGregory(
+ OSD_INOUT_ARRAY(OSD_REAL, wB, 15),
+ OSD_INOUT_ARRAY(OSD_REAL, rG, 6),
+ OSD_OUT_ARRAY(OSD_REAL, wG, 18)) {
+
+ wG[0] = wB[0];
+ wG[1] = wB[1];
+ wG[2] = wB[5];
+ wG[3] = wB[6] * rG[0];
+ wG[4] = wB[6] * rG[1];
+
+ wG[5] = wB[4];
+ wG[6] = wB[8];
+ wG[7] = wB[3];
+ wG[8] = wB[7] * rG[2];
+ wG[9] = wB[7] * rG[3];
+
+ wG[10] = wB[14];
+ wG[11] = wB[12];
+ wG[12] = wB[13];
+ wG[13] = wB[10] * rG[4];
+ wG[14] = wB[10] * rG[5];
+
+ wG[15] = wB[2];
+ wG[16] = wB[11];
+ wG[17] = wB[9];
+ }
+// } // end namespace
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+Osd_EvalBasisGregoryTri(OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 18),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 18)) {
+
+ //
+ // Bezier basis functions are denoted with B while the rational multipliers for the
+ // interior points will be denoted G -- so we have B(s,t) and G(s,t) (though we
+ // switch to barycentric (u,v,w) briefly to compute G)
+ //
+ OSD_REAL BP[15], BDs[15], BDt[15], BDss[15], BDst[15], BDtt[15];
+
+ OSD_REAL G[6] = OSD_ARRAY_6(OSD_REAL, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f );
+ OSD_REAL u = s;
+ OSD_REAL v = t;
+ OSD_REAL w = 1 - u - v;
+
+ if ((u + v) > 0) {
+ G[0] = u / (u + v);
+ G[1] = v / (u + v);
+ }
+ if ((v + w) > 0) {
+ G[2] = v / (v + w);
+ G[3] = w / (v + w);
+ }
+ if ((w + u) > 0) {
+ G[4] = w / (w + u);
+ G[5] = u / (w + u);
+ }
+
+ //
+ // Compute Bezier basis functions and convert, adjusting interior points:
+ //
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBezierTriDerivWeights(s, t, 0, 0, BP);
+ Osd_convertBezierWeightsToGregory(BP, G, wP);
+ }
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // TBD -- ifdef OPENSUBDIV_GREGORY_EVAL_TRUE_DERIVATIVES
+
+ Osd_evalBezierTriDerivWeights(s, t, 1, 0, BDs);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 1, BDt);
+
+ Osd_convertBezierWeightsToGregory(BDs, G, wDs);
+ Osd_convertBezierWeightsToGregory(BDt, G, wDt);
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ Osd_evalBezierTriDerivWeights(s, t, 2, 0, BDss);
+ Osd_evalBezierTriDerivWeights(s, t, 1, 1, BDst);
+ Osd_evalBezierTriDerivWeights(s, t, 0, 2, BDtt);
+
+ Osd_convertBezierWeightsToGregory(BDss, G, wDss);
+ Osd_convertBezierWeightsToGregory(BDst, G, wDst);
+ Osd_convertBezierWeightsToGregory(BDtt, G, wDtt);
+ }
+ }
+ return 18;
+}
+
+// The following functions are low-level internal methods which
+// were exposed in earlier releases, but were never intended to
+// be part of the supported public API.
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBezierCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplineWeights(
+ OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDP2, 4)) {
+
+ Osd_evalBSplineCurve(t, wP, wDP, wDP2);
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBoxSplineWeights(
+ float s, float t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 12)) {
+
+ OSD_REAL stMonomials[15];
+ Osd_evalBivariateMonomialsQuartic(s, t, stMonomials);
+
+ if (OSD_OPTIONAL(wP)) {
+ Osd_evalBoxSplineTriDerivWeights(stMonomials, 0, 0, wP);
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdAdjustBoundaryWeights(
+ int boundary,
+ OSD_INOUT_ARRAY(OSD_REAL, sWeights, 4),
+ OSD_INOUT_ARRAY(OSD_REAL, tWeights, 4)) {
+
+ if ((boundary & 1) != 0) {
+ tWeights[2] -= tWeights[0];
+ tWeights[1] += tWeights[0] * 2.0f;
+ tWeights[0] = 0.0f;
+ }
+ if ((boundary & 2) != 0) {
+ sWeights[1] -= sWeights[3];
+ sWeights[2] += sWeights[3] * 2.0f;
+ sWeights[3] = 0.0f;
+ }
+ if ((boundary & 4) != 0) {
+ tWeights[1] -= tWeights[3];
+ tWeights[2] += tWeights[3] * 2.0f;
+ tWeights[3] = 0.0f;
+ }
+ if ((boundary & 8) != 0) {
+ sWeights[2] -= sWeights[0];
+ sWeights[1] += sWeights[0] * 2.0f;
+ sWeights[0] = 0.0f;
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdComputeTensorProductPatchWeights(
+ float dScale, int boundary,
+ OSD_IN_ARRAY(float, sWeights, 4),
+ OSD_IN_ARRAY(float, tWeights, 4),
+ OSD_IN_ARRAY(float, dsWeights, 4),
+ OSD_IN_ARRAY(float, dtWeights, 4),
+ OSD_IN_ARRAY(float, dssWeights, 4),
+ OSD_IN_ARRAY(float, dttWeights, 4),
+ OSD_OUT_ARRAY(float, wP, 16),
+ OSD_OUT_ARRAY(float, wDs, 16),
+ OSD_OUT_ARRAY(float, wDt, 16),
+ OSD_OUT_ARRAY(float, wDss, 16),
+ OSD_OUT_ARRAY(float, wDst, 16),
+ OSD_OUT_ARRAY(float, wDtt, 16)) {
+
+ if (OSD_OPTIONAL(wP)) {
+ // Compute the tensor product weight of the (s,t) basis function
+ // corresponding to each control vertex:
+
+ OsdAdjustBoundaryWeights(boundary, sWeights, tWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wP[4*i+j] = sWeights[j] * tWeights[i];
+ }
+ }
+ }
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ // Compute the tensor product weight of the differentiated (s,t) basis
+ // function corresponding to each control vertex (scaled accordingly):
+
+ OsdAdjustBoundaryWeights(boundary, dsWeights, dtWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDs[4*i+j] = dsWeights[j] * tWeights[i] * dScale;
+ wDt[4*i+j] = sWeights[j] * dtWeights[i] * dScale;
+ }
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ // Compute the tensor product weight of appropriate differentiated
+ // (s,t) basis functions for each control vertex (scaled accordingly):
+ float d2Scale = dScale * dScale;
+
+ OsdAdjustBoundaryWeights(boundary, dssWeights, dttWeights);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ wDss[4*i+j] = dssWeights[j] * tWeights[i] * d2Scale;
+ wDst[4*i+j] = dsWeights[j] * dtWeights[i] * d2Scale;
+ wDtt[4*i+j] = sWeights[j] * dttWeights[i] * d2Scale;
+ }
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBilinearPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 4),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 4)) {
+
+ int nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBSplinePatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale, int boundary,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+
+ int nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ Osd_boundBasisBSpline(boundary, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetBezierPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 16),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 16)) {
+ int nPoints = Osd_EvalBasisBezier(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+// Deprecated -- prefer use of OsdEvaluatePatchBasis() and
+// OsdEvaluatePatchBasisNormalized() methods.
+OSD_FUNCTION_STORAGE_CLASS
+void
+OsdGetGregoryPatchWeights(
+ OSD_REAL s, OSD_REAL t, OSD_REAL d1Scale,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+ int nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_H */
+
+//
+// Copyright 2018 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_OSD_PATCH_BASIS_COMMON_EVAL_H
+#define OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasisNormalized(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ int boundaryMask = OsdPatchParamGetBoundary(param);
+
+ int nPoints = 0;
+ if (patchType == OSD_PATCH_DESCRIPTOR_REGULAR) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBSpline(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP16[16], wDs16[16], wDt16[16],
+ wDss16[16], wDst16[16], wDtt16[16];
+ nPoints = Osd_EvalBasisBSpline(
+ s, t, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBSpline(
+ boundaryMask, wP16, wDs16, wDt16, wDss16, wDst16, wDtt16);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP16[i];
+ wDs[i] = wDs16[i]; wDt[i] = wDt16[i];
+ wDss[i] = wDss16[i]; wDst[i] = wDst16[i]; wDtt[i] = wDtt16[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_LOOP) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP, wDs, wDt, wDss, wDst, wDtt);
+ }
+#else
+ OSD_REAL wP12[12], wDs12[12], wDt12[12],
+ wDss12[12], wDst12[12], wDtt12[12];
+ nPoints = Osd_EvalBasisBoxSplineTri(
+ s, t, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ if (boundaryMask != 0) {
+ Osd_boundBasisBoxSplineTri(
+ boundaryMask, wP12, wDs12, wDt12, wDss12, wDst12, wDtt12);
+ }
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP12[i];
+ wDs[i] = wDs12[i]; wDt[i] = wDt12[i];
+ wDss[i] = wDss12[i]; wDst[i] = wDst12[i]; wDtt[i] = wDtt12[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_BASIS) {
+ nPoints = Osd_EvalBasisGregory(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisGregoryTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP18[18], wDs18[18], wDt18[18],
+ wDss18[18], wDst18[18], wDtt18[18];
+ nPoints = Osd_EvalBasisGregoryTri(
+ s, t, wP18, wDs18, wDt18, wDss18, wDst18, wDtt18);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP18[i];
+ wDs[i] = wDs18[i]; wDt[i] = wDt18[i];
+ wDss[i] = wDss18[i]; wDst[i] = wDst18[i]; wDtt[i] = wDtt18[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_QUADS) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinear(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP4[4], wDs4[4], wDt4[4],
+ wDss4[4], wDst4[4], wDtt4[4];
+ nPoints = Osd_EvalBasisLinear(
+ s, t, wP4, wDs4, wDt4, wDss4, wDst4, wDtt4);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP4[i];
+ wDs[i] = wDs4[i]; wDt[i] = wDt4[i];
+ wDss[i] = wDss4[i]; wDst[i] = wDst4[i]; wDtt[i] = wDtt4[i];
+ }
+#endif
+ } else if (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES) {
+#if OSD_ARRAY_ARG_BOUND_OPTIONAL
+ nPoints = Osd_EvalBasisLinearTri(s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+#else
+ OSD_REAL wP3[3], wDs3[3], wDt3[3],
+ wDss3[3], wDst3[3], wDtt3[3];
+ nPoints = Osd_EvalBasisLinearTri(
+ s, t, wP3, wDs3, wDt3, wDss3, wDst3, wDtt3);
+ for (int i=0; i<nPoints; ++i) {
+ wP[i] = wP3[i];
+ wDs[i] = wDs3[i]; wDt[i] = wDt3[i];
+ wDss[i] = wDss3[i]; wDst[i] = wDst3[i]; wDtt[i] = wDtt3[i];
+ }
+#endif
+ } else {
+ // assert(0);
+ }
+ return nPoints;
+}
+
+OSD_FUNCTION_STORAGE_CLASS
+// template <typename REAL>
+int
+OsdEvaluatePatchBasis(
+ int patchType, OsdPatchParam param,
+ OSD_REAL s, OSD_REAL t,
+ OSD_OUT_ARRAY(OSD_REAL, wP, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDs, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDt, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDss, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDst, 20),
+ OSD_OUT_ARRAY(OSD_REAL, wDtt, 20)) {
+
+ OSD_REAL derivSign = 1.0f;
+
+ if ((patchType == OSD_PATCH_DESCRIPTOR_LOOP) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_GREGORY_TRIANGLE) ||
+ (patchType == OSD_PATCH_DESCRIPTOR_TRIANGLES)) {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalizeTriangle(param, uv);
+ s = uv[0];
+ t = uv[1];
+ if (OsdPatchParamIsTriangleRotated(param)) {
+ derivSign = -1.0f;
+ }
+ } else {
+ OSD_REAL uv[2] = OSD_ARRAY_2(OSD_REAL, s, t);
+ OsdPatchParamNormalize(param, uv);
+ s = uv[0];
+ t = uv[1];
+ }
+
+ int nPoints = OsdEvaluatePatchBasisNormalized(
+ patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
+
+ if (OSD_OPTIONAL(wDs && wDt)) {
+ OSD_REAL d1Scale =
+ derivSign * OSD_REAL_CAST(1 << OsdPatchParamGetDepth(param));
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDs[i] *= d1Scale;
+ wDt[i] *= d1Scale;
+ }
+
+ if (OSD_OPTIONAL(wDss && wDst && wDtt)) {
+ OSD_REAL d2Scale = derivSign * d1Scale * d1Scale;
+
+ for (int i = 0; i < nPoints; ++i) {
+ wDss[i] *= d2Scale;
+ wDst[i] *= d2Scale;
+ wDtt[i] *= d2Scale;
+ }
+ }
+ }
+ return nPoints;
+}
+
+#endif /* OPENSUBDIV3_OSD_PATCH_BASIS_COMMON_EVAL_H */
+
+//
+// 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.
+//
+
+//
+// typical shader composition ordering (see glDrawRegistry:_CompileShader)
+//
+//
+// - glsl version string (#version 430)
+//
+// - common defines (#define OSD_ENABLE_PATCH_CULL, ...)
+// - source defines (#define VERTEX_SHADER, ...)
+//
+// - osd headers (glslPatchCommon: varying structs,
+// glslPtexCommon: ptex functions)
+// - client header (Osd*Matrix(), displacement callback, ...)
+//
+// - osd shader source (glslPatchBSpline, glslPatchGregory, ...)
+// or
+// client shader source (vertex/geometry/fragment shader)
+//
+
+//----------------------------------------------------------
+// Patches.Common
+//----------------------------------------------------------
+
+// XXXdyu all handling of varying data can be managed by client code
+#ifndef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE
+// type var;
+#endif
+
+#ifndef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+// layout(location = loc) in type var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX()
+// output.var = var;
+#endif
+
+#ifndef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+// output[ID_OUT].var = input[ID_IN].var
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+// output.var =
+// mix(mix(input[a].var, input[b].var, UV.x),
+// mix(input[c].var, input[d].var, UV.x), UV.y)
+#endif
+
+#ifndef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+// output.var =
+// input[a].var * (1.0f-UV.x-UV.y) +
+// input[b].var * UV.x +
+// input[c].var * UV.y;
+#endif
+
+#if __VERSION__ < 420
+ #define centroid
+#endif
+
+struct ControlVertex {
+ vec4 position;
+#ifdef OSD_ENABLE_PATCH_CULL
+ ivec3 clipFlag;
+#endif
+};
+
+// XXXdyu all downstream data can be handled by client code
+struct OutputVertex {
+ vec4 position;
+ vec3 normal;
+ vec3 tangent;
+ vec3 bitangent;
+ vec4 patchCoord; // u, v, faceLevel, faceId
+ vec2 tessCoord; // tesscoord.st
+#if defined OSD_COMPUTE_NORMAL_DERIVATIVES
+ vec3 Nu;
+ vec3 Nv;
+#endif
+};
+
+// osd shaders need following functions defined
+mat4 OsdModelViewMatrix();
+mat4 OsdProjectionMatrix();
+mat4 OsdModelViewProjectionMatrix();
+float OsdTessLevel();
+int OsdGregoryQuadOffsetBase();
+int OsdPrimitiveIdBase();
+int OsdBaseVertex();
+
+#ifndef OSD_DISPLACEMENT_CALLBACK
+#define OSD_DISPLACEMENT_CALLBACK
+#endif
+
+// ----------------------------------------------------------------------------
+// Patch Parameters
+// ----------------------------------------------------------------------------
+
+//
+// Each patch has a corresponding patchParam. This is a set of three values
+// specifying additional information about the patch:
+//
+// faceId -- topological face identifier (e.g. Ptex FaceId)
+// bitfield -- refinement-level, non-quad, boundary, transition, uv-offset
+// sharpness -- crease sharpness for single-crease patches
+//
+// These are stored in OsdPatchParamBuffer indexed by the value returned
+// from OsdGetPatchIndex() which is a function of the current PrimitiveID
+// along with an optional client provided offset.
+//
+
+uniform isamplerBuffer OsdPatchParamBuffer;
+
+int OsdGetPatchIndex(int primitiveId)
+{
+ return (primitiveId + OsdPrimitiveIdBase());
+}
+
+ivec3 OsdGetPatchParam(int patchIndex)
+{
+ return texelFetch(OsdPatchParamBuffer, patchIndex).xyz;
+}
+
+int OsdGetPatchFaceId(ivec3 patchParam)
+{
+ return (patchParam.x & 0xfffffff);
+}
+
+int OsdGetPatchFaceLevel(ivec3 patchParam)
+{
+ return (1 << ((patchParam.y & 0xf) - ((patchParam.y >> 4) & 1)));
+}
+
+int OsdGetPatchRefinementLevel(ivec3 patchParam)
+{
+ return (patchParam.y & 0xf);
+}
+
+int OsdGetPatchBoundaryMask(ivec3 patchParam)
+{
+ return ((patchParam.y >> 7) & 0x1f);
+}
+
+int OsdGetPatchTransitionMask(ivec3 patchParam)
+{
+ return ((patchParam.x >> 28) & 0xf);
+}
+
+ivec2 OsdGetPatchFaceUV(ivec3 patchParam)
+{
+ int u = (patchParam.y >> 22) & 0x3ff;
+ int v = (patchParam.y >> 12) & 0x3ff;
+ return ivec2(u,v);
+}
+
+bool OsdGetPatchIsRegular(ivec3 patchParam)
+{
+ return ((patchParam.y >> 5) & 0x1) != 0;
+}
+
+bool OsdGetPatchIsTriangleRotated(ivec3 patchParam)
+{
+ ivec2 uv = OsdGetPatchFaceUV(patchParam);
+ return (uv.x + uv.y) >= OsdGetPatchFaceLevel(patchParam);
+}
+
+float OsdGetPatchSharpness(ivec3 patchParam)
+{
+ return intBitsToFloat(patchParam.z);
+}
+
+float OsdGetPatchSingleCreaseSegmentParameter(ivec3 patchParam, vec2 uv)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ float s = 0;
+ if ((boundaryMask & 1) != 0) {
+ s = 1 - uv.y;
+ } else if ((boundaryMask & 2) != 0) {
+ s = uv.x;
+ } else if ((boundaryMask & 4) != 0) {
+ s = uv.y;
+ } else if ((boundaryMask & 8) != 0) {
+ s = 1 - uv.x;
+ }
+ return s;
+}
+
+ivec4 OsdGetPatchCoord(ivec3 patchParam)
+{
+ int faceId = OsdGetPatchFaceId(patchParam);
+ int faceLevel = OsdGetPatchFaceLevel(patchParam);
+ ivec2 faceUV = OsdGetPatchFaceUV(patchParam);
+ return ivec4(faceUV.x, faceUV.y, faceLevel, faceId);
+}
+
+vec4 OsdInterpolatePatchCoord(vec2 localUV, ivec3 patchParam)
+{
+ ivec4 perPrimPatchCoord = OsdGetPatchCoord(patchParam);
+ int faceId = perPrimPatchCoord.w;
+ int faceLevel = perPrimPatchCoord.z;
+ vec2 faceUV = vec2(perPrimPatchCoord.x, perPrimPatchCoord.y);
+ vec2 uv = localUV/faceLevel + faceUV/faceLevel;
+ // add 0.5 to integer values for more robust interpolation
+ return vec4(uv.x, uv.y, faceLevel+0.5f, faceId+0.5f);
+}
+
+vec4 OsdInterpolatePatchCoordTriangle(vec2 localUV, ivec3 patchParam)
+{
+ vec4 result = OsdInterpolatePatchCoord(localUV, patchParam);
+ if (OsdGetPatchIsTriangleRotated(patchParam)) {
+ result.xy = vec2(1.0f) - result.xy;
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// patch culling
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_ENABLE_PATCH_CULL
+
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P) vec4 clipPos = OsdModelViewProjectionMatrix() * P; bvec3 clip0 = lessThan(clipPos.xyz, vec3(clipPos.w)); bvec3 clip1 = greaterThan(clipPos.xyz, -vec3(clipPos.w)); outpt.v.clipFlag = ivec3(clip0) + 2*ivec3(clip1);
+#define OSD_PATCH_CULL(N) ivec3 clipFlag = ivec3(0); for(int i = 0; i < N; ++i) { clipFlag |= inpt[i].v.clipFlag; } if (clipFlag != ivec3(3) ) { gl_TessLevelInner[0] = 0; gl_TessLevelInner[1] = 0; gl_TessLevelOuter[0] = 0; gl_TessLevelOuter[1] = 0; gl_TessLevelOuter[2] = 0; gl_TessLevelOuter[3] = 0; return; }
+
+#else
+#define OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(P)
+#define OSD_PATCH_CULL(N)
+#endif
+
+// ----------------------------------------------------------------------------
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+}
+
+void
+OsdUnivar4x4(in float u, out float B[4], out float D[4], out float C[4])
+{
+ float t = u;
+ float s = 1.0f - u;
+
+ float A0 = s * s;
+ float A1 = 2 * s * t;
+ float A2 = t * t;
+
+ B[0] = s * A0;
+ B[1] = t * A0 + s * A1;
+ B[2] = t * A1 + s * A2;
+ B[3] = t * A2;
+
+ D[0] = - A0;
+ D[1] = A0 - A1;
+ D[2] = A1 - A2;
+ D[3] = A2;
+
+ A0 = - s;
+ A1 = s - t;
+ A2 = t;
+
+ C[0] = - A0;
+ C[1] = A0 - A1;
+ C[2] = A1 - A2;
+ C[3] = A2;
+}
+
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexBezier {
+ ivec3 patchParam;
+ vec3 P;
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec3 P1;
+ vec3 P2;
+ vec2 vSegments;
+#endif
+};
+
+vec3
+OsdEvalBezier(vec3 cp[16], vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+
+ OsdUnivar4x4(uv.x, B, D);
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j];
+ BUCP[i] += A * B[j];
+ }
+ }
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// When OSD_PATCH_ENABLE_SINGLE_CREASE is defined,
+// this function evaluates single-crease patch, which is segmented into
+// 3 parts in the v-direction.
+//
+// v=0 vSegment.x vSegment.y v=1
+// +------------------+-------------------+------------------+
+// | cp 0 | cp 1 | cp 2 |
+// | (infinite sharp) | (floor sharpness) | (ceil sharpness) |
+// +------------------+-------------------+------------------+
+//
+vec3
+OsdEvalBezier(OsdPerPatchVertexBezier cp[16], ivec3 patchParam, vec2 uv)
+{
+ vec3 BUCP[4] = vec3[4](vec3(0), vec3(0), vec3(0), vec3(0));
+
+ float B[4], D[4];
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, uv);
+
+ OsdUnivar4x4(uv.x, B, D);
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments = cp[0].vSegments;
+ if (s <= vSegments.x) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else if (s <= vSegments.y) {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P1;
+ BUCP[i] += A * B[j];
+ }
+ }
+ } else {
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P2;
+ BUCP[i] += A * B[j];
+ }
+ }
+ }
+#else
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+ vec3 A = cp[4*i + j].P;
+ BUCP[i] += A * B[j];
+ }
+ }
+#endif
+
+ vec3 P = vec3(0);
+
+ OsdUnivar4x4(uv.y, B, D);
+ for (int k=0; k<4; ++k) {
+ P += B[k] * BUCP[k];
+ }
+
+ return P;
+}
+
+// ----------------------------------------------------------------------------
+// Boundary Interpolation
+// ----------------------------------------------------------------------------
+
+void
+OsdComputeBSplineBoundaryPoints(inout vec3 cpt[16], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+
+ // Don't extrapolate corner points until all boundary points in place
+ if ((boundaryMask & 1) != 0) {
+ cpt[1] = 2*cpt[5] - cpt[9];
+ cpt[2] = 2*cpt[6] - cpt[10];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[7] = 2*cpt[6] - cpt[5];
+ cpt[11] = 2*cpt[10] - cpt[9];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[13] = 2*cpt[9] - cpt[5];
+ cpt[14] = 2*cpt[10] - cpt[6];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[4] = 2*cpt[5] - cpt[6];
+ cpt[8] = 2*cpt[9] - cpt[10];
+ }
+
+ // Now safe to extrapolate corner points:
+ if ((boundaryMask & 1) != 0) {
+ cpt[0] = 2*cpt[4] - cpt[8];
+ cpt[3] = 2*cpt[7] - cpt[11];
+ }
+ if ((boundaryMask & 2) != 0) {
+ cpt[3] = 2*cpt[2] - cpt[1];
+ cpt[15] = 2*cpt[14] - cpt[13];
+ }
+ if ((boundaryMask & 4) != 0) {
+ cpt[12] = 2*cpt[8] - cpt[4];
+ cpt[15] = 2*cpt[11] - cpt[7];
+ }
+ if ((boundaryMask & 8) != 0) {
+ cpt[0] = 2*cpt[1] - cpt[2];
+ cpt[12] = 2*cpt[13] - cpt[14];
+ }
+}
+
+void
+OsdComputeBoxSplineTriangleBoundaryPoints(inout vec3 cpt[12], ivec3 patchParam)
+{
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if (boundaryMask == 0) return;
+
+ int upperBits = (boundaryMask >> 3) & 0x3;
+ int lowerBits = boundaryMask & 7;
+
+ int eBits = lowerBits;
+ int vBits = 0;
+
+ if (upperBits == 1) {
+ vBits = eBits;
+ eBits = 0;
+ } else if (upperBits == 2) {
+ // Opposite vertex bit is edge bit rotated one to the right:
+ vBits = ((eBits & 1) << 2) | (eBits >> 1);
+ }
+
+ bool edge0IsBoundary = (eBits & 1) != 0;
+ bool edge1IsBoundary = (eBits & 2) != 0;
+ bool edge2IsBoundary = (eBits & 4) != 0;
+
+ if (edge0IsBoundary) {
+ if (edge2IsBoundary) {
+ cpt[0] = cpt[4] + (cpt[4] - cpt[8]);
+ } else {
+ cpt[0] = cpt[4] + (cpt[3] - cpt[7]);
+ }
+ cpt[1] = cpt[4] + cpt[5] - cpt[8];
+ if (edge1IsBoundary) {
+ cpt[2] = cpt[5] + (cpt[5] - cpt[8]);
+ } else {
+ cpt[2] = cpt[5] + (cpt[6] - cpt[9]);
+ }
+ }
+ if (edge1IsBoundary) {
+ if (edge0IsBoundary) {
+ cpt[6] = cpt[5] + (cpt[5] - cpt[4]);
+ } else {
+ cpt[6] = cpt[5] + (cpt[2] - cpt[1]);
+ }
+ cpt[9] = cpt[5] + cpt[8] - cpt[4];
+ if (edge2IsBoundary) {
+ cpt[11] = cpt[8] + (cpt[8] - cpt[4]);
+ } else {
+ cpt[11] = cpt[8] + (cpt[10] - cpt[7]);
+ }
+ }
+ if (edge2IsBoundary) {
+ if (edge1IsBoundary) {
+ cpt[10] = cpt[8] + (cpt[8] - cpt[5]);
+ } else {
+ cpt[10] = cpt[8] + (cpt[11] - cpt[9]);
+ }
+ cpt[7] = cpt[8] + cpt[4] - cpt[5];
+ if (edge0IsBoundary) {
+ cpt[3] = cpt[4] + (cpt[4] - cpt[5]);
+ } else {
+ cpt[3] = cpt[4] + (cpt[0] - cpt[1]);
+ }
+ }
+
+ if ((vBits & 1) != 0) {
+ cpt[3] = cpt[4] + cpt[7] - cpt[8];
+ cpt[0] = cpt[4] + cpt[1] - cpt[5];
+ }
+ if ((vBits & 2) != 0) {
+ cpt[2] = cpt[5] + cpt[1] - cpt[4];
+ cpt[6] = cpt[5] + cpt[9] - cpt[8];
+ }
+ if ((vBits & 4) != 0) {
+ cpt[11] = cpt[8] + cpt[9] - cpt[5];
+ cpt[10] = cpt[8] + cpt[7] - cpt[4];
+ }
+}
+
+// ----------------------------------------------------------------------------
+// BSpline
+// ----------------------------------------------------------------------------
+
+// compute single-crease patch matrix
+mat4
+OsdComputeMs(float sharpness)
+{
+ float s = pow(2.0f, sharpness);
+ float s2 = s*s;
+ float s3 = s2*s;
+
+ mat4 m = mat4(
+ 0, s + 1 + 3*s2 - s3, 7*s - 2 - 6*s2 + 2*s3, (1-s)*(s-1)*(s-1),
+ 0, (1+s)*(1+s), 6*s - 2 - 2*s2, (s-1)*(s-1),
+ 0, 1+s, 6*s - 2, 1-s,
+ 0, 1, 6*s - 2, 1);
+
+ m /= (s*6.0);
+ m[0][0] = 1.0/6.0;
+
+ return m;
+}
+
+// flip matrix orientation
+mat4
+OsdFlipMatrix(mat4 m)
+{
+ return mat4(m[3][3], m[3][2], m[3][1], m[3][0],
+ m[2][3], m[2][2], m[2][1], m[2][0],
+ m[1][3], m[1][2], m[1][1], m[1][0],
+ m[0][3], m[0][2], m[0][1], m[0][0]);
+}
+
+// Regular BSpline to Bezier
+uniform mat4 Q = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 1.f/6.f, 4.f/6.f, 1.f/6.f
+);
+
+// Infinitely Sharp (boundary)
+uniform mat4 Mi = mat4(
+ 1.f/6.f, 4.f/6.f, 1.f/6.f, 0.f,
+ 0.f, 4.f/6.f, 2.f/6.f, 0.f,
+ 0.f, 2.f/6.f, 4.f/6.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f
+);
+
+// convert BSpline cv to Bezier cv
+void
+OsdComputePerPatchVertexBSpline(ivec3 patchParam, int ID, vec3 cv[16],
+ out OsdPerPatchVertexBezier result)
+{
+ result.patchParam = patchParam;
+
+ int i = ID%4;
+ int j = ID/4;
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+
+ vec3 P = vec3(0); // 0 to 1-2^(-Sf)
+ vec3 P1 = vec3(0); // 1-2^(-Sf) to 1-2^(-Sc)
+ vec3 P2 = vec3(0); // 1-2^(-Sc) to 1
+
+ float sharpness = OsdGetPatchSharpness(patchParam);
+ if (sharpness > 0) {
+ float Sf = floor(sharpness);
+ float Sc = ceil(sharpness);
+ float Sr = fract(sharpness);
+ mat4 Mf = OsdComputeMs(Sf);
+ mat4 Mc = OsdComputeMs(Sc);
+ mat4 Mj = (1-Sr) * Mf + Sr * Mi;
+ mat4 Ms = (1-Sr) * Mf + Sr * Mc;
+ float s0 = 1 - pow(2, -floor(sharpness));
+ float s1 = 1 - pow(2, -ceil(sharpness));
+ result.vSegments = vec2(s0, s1);
+
+ mat4 MUi = Q, MUj = Q, MUs = Q;
+ mat4 MVi = Q, MVj = Q, MVs = Q;
+
+ int boundaryMask = OsdGetPatchBoundaryMask(patchParam);
+ if ((boundaryMask & 1) != 0) {
+ MVi = OsdFlipMatrix(Mi);
+ MVj = OsdFlipMatrix(Mj);
+ MVs = OsdFlipMatrix(Ms);
+ }
+ if ((boundaryMask & 2) != 0) {
+ MUi = Mi;
+ MUj = Mj;
+ MUs = Ms;
+ }
+ if ((boundaryMask & 4) != 0) {
+ MVi = Mi;
+ MVj = Mj;
+ MVs = Ms;
+ }
+ if ((boundaryMask & 8) != 0) {
+ MUi = OsdFlipMatrix(Mi);
+ MUj = OsdFlipMatrix(Mj);
+ MUs = OsdFlipMatrix(Ms);
+ }
+
+ vec3 Hi[4], Hj[4], Hs[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = Hj[l] = Hs[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += MUi[i][k] * cv[l*4 + k];
+ Hj[l] += MUj[i][k] * cv[l*4 + k];
+ Hs[l] += MUs[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += MVi[j][k]*Hi[k];
+ P1 += MVj[j][k]*Hj[k];
+ P2 += MVs[j][k]*Hs[k];
+ }
+
+ result.P = P;
+ result.P1 = P1;
+ result.P2 = P2;
+ } else {
+ result.vSegments = vec2(0);
+
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 Hi[4];
+ for (int l=0; l<4; ++l) {
+ Hi[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ Hi[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ for (int k=0; k<4; ++k) {
+ P += Q[j][k]*Hi[k];
+ }
+
+ result.P = P;
+ result.P1 = P;
+ result.P2 = P;
+ }
+#else
+ OsdComputeBSplineBoundaryPoints(cv, patchParam);
+
+ vec3 H[4];
+ for (int l=0; l<4; ++l) {
+ H[l] = vec3(0);
+ for (int k=0; k<4; ++k) {
+ H[l] += Q[i][k] * cv[l*4 + k];
+ }
+ }
+ {
+ result.P = vec3(0);
+ for (int k=0; k<4; ++k) {
+ result.P += Q[j][k]*H[k];
+ }
+ }
+#endif
+}
+
+void
+OsdEvalPatchBezier(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[16],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ //
+ // Use the recursive nature of the basis functions to compute a 2x2 set
+ // of intermediate points (via repeated linear interpolation). These
+ // points define a bilinear surface tangent to the desired surface at P
+ // and so containing dPu and dPv. The cost of computing P, dPu and dPv
+ // this way is comparable to that of typical tensor product evaluation
+ // (if not faster).
+ //
+ // If N = dPu X dPv degenerates, it often results from an edge of the
+ // 2x2 bilinear hull collapsing or two adjacent edges colinear. In both
+ // cases, the expected non-planar quad degenerates into a triangle, and
+ // the tangent plane of that triangle provides the desired normal N.
+ //
+
+ // Reduce 4x4 points to 2x4 -- two levels of linear interpolation in U
+ // and so 3 original rows contributing to each of the 2 resulting rows:
+ float u = UV.x;
+ float uinv = 1.0f - u;
+
+ float u0 = uinv * uinv;
+ float u1 = u * uinv * 2.0f;
+ float u2 = u * u;
+
+ vec3 LROW[4], RROW[4];
+#ifndef OSD_PATCH_ENABLE_SINGLE_CREASE
+ LROW[0] = u0 * cv[ 0].P + u1 * cv[ 1].P + u2 * cv[ 2].P;
+ LROW[1] = u0 * cv[ 4].P + u1 * cv[ 5].P + u2 * cv[ 6].P;
+ LROW[2] = u0 * cv[ 8].P + u1 * cv[ 9].P + u2 * cv[10].P;
+ LROW[3] = u0 * cv[12].P + u1 * cv[13].P + u2 * cv[14].P;
+
+ RROW[0] = u0 * cv[ 1].P + u1 * cv[ 2].P + u2 * cv[ 3].P;
+ RROW[1] = u0 * cv[ 5].P + u1 * cv[ 6].P + u2 * cv[ 7].P;
+ RROW[2] = u0 * cv[ 9].P + u1 * cv[10].P + u2 * cv[11].P;
+ RROW[3] = u0 * cv[13].P + u1 * cv[14].P + u2 * cv[15].P;
+#else
+ vec2 vSegments = cv[0].vSegments;
+ float s = OsdGetPatchSingleCreaseSegmentParameter(patchParam, UV);
+
+ for (int i = 0; i < 4; ++i) {
+ int j = i*4;
+ if (s <= vSegments.x) {
+ LROW[i] = u0 * cv[ j ].P + u1 * cv[j+1].P + u2 * cv[j+2].P;
+ RROW[i] = u0 * cv[j+1].P + u1 * cv[j+2].P + u2 * cv[j+3].P;
+ } else if (s <= vSegments.y) {
+ LROW[i] = u0 * cv[ j ].P1 + u1 * cv[j+1].P1 + u2 * cv[j+2].P1;
+ RROW[i] = u0 * cv[j+1].P1 + u1 * cv[j+2].P1 + u2 * cv[j+3].P1;
+ } else {
+ LROW[i] = u0 * cv[ j ].P2 + u1 * cv[j+1].P2 + u2 * cv[j+2].P2;
+ RROW[i] = u0 * cv[j+1].P2 + u1 * cv[j+2].P2 + u2 * cv[j+3].P2;
+ }
+ }
+#endif
+
+ // Reduce 2x4 points to 2x2 -- two levels of linear interpolation in V
+ // and so 3 original pairs contributing to each of the 2 resulting:
+ float v = UV.y;
+ float vinv = 1.0f - v;
+
+ float v0 = vinv * vinv;
+ float v1 = v * vinv * 2.0f;
+ float v2 = v * v;
+
+ vec3 LPAIR[2], RPAIR[2];
+ LPAIR[0] = v0 * LROW[0] + v1 * LROW[1] + v2 * LROW[2];
+ RPAIR[0] = v0 * RROW[0] + v1 * RROW[1] + v2 * RROW[2];
+
+ LPAIR[1] = v0 * LROW[1] + v1 * LROW[2] + v2 * LROW[3];
+ RPAIR[1] = v0 * RROW[1] + v1 * RROW[2] + v2 * RROW[3];
+
+ // Interpolate points on the edges of the 2x2 bilinear hull from which
+ // both position and partials are trivially determined:
+ vec3 DU0 = vinv * LPAIR[0] + v * LPAIR[1];
+ vec3 DU1 = vinv * RPAIR[0] + v * RPAIR[1];
+ vec3 DV0 = uinv * LPAIR[0] + u * RPAIR[0];
+ vec3 DV1 = uinv * LPAIR[1] + u * RPAIR[1];
+
+ int level = OsdGetPatchFaceLevel(patchParam);
+ dPu = (DU1 - DU0) * 3 * level;
+ dPv = (DV1 - DV0) * 3 * level;
+
+ P = u * DU1 + uinv * DU0;
+
+ // Compute the normal and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+
+ float nLength = length(N);
+ if (nLength > nEpsilon) {
+ N = N / nLength;
+ } else {
+ vec3 diagCross = cross(RPAIR[1] - LPAIR[0], LPAIR[1] - RPAIR[0]);
+ float diagCrossLength = length(diagCross);
+ if (diagCrossLength > nEpsilon) {
+ N = diagCross / diagCrossLength;
+ }
+ }
+
+#ifndef OSD_COMPUTE_NORMAL_DERIVATIVES
+ dNu = vec3(0);
+ dNv = vec3(0);
+#else
+ //
+ // Compute 2nd order partials of P(u,v) in order to compute 1st order partials
+ // for the un-normalized n(u,v) = dPu X dPv, then project into the tangent
+ // plane of normalized N. With resulting dNu and dNv we can make another
+ // attempt to resolve a still-degenerate normal.
+ //
+ // We don't use the Weingarten equations here as they require N != 0 and also
+ // are a little less numerically stable/accurate in single precision.
+ //
+ float B0u[4], B1u[4], B2u[4];
+ float B0v[4], B1v[4], B2v[4];
+
+ OsdUnivar4x4(UV.x, B0u, B1u, B2u);
+ OsdUnivar4x4(UV.y, B0v, B1v, B2v);
+
+ vec3 dUU = vec3(0);
+ vec3 dVV = vec3(0);
+ vec3 dUV = vec3(0);
+
+ for (int i=0; i<4; ++i) {
+ for (int j=0; j<4; ++j) {
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ int k = 4*i + j;
+ vec3 CV = (s <= vSegments.x) ? cv[k].P
+ : ((s <= vSegments.y) ? cv[k].P1
+ : cv[k].P2);
+#else
+ vec3 CV = cv[4*i + j].P;
+#endif
+ dUU += (B0v[i] * B2u[j]) * CV;
+ dVV += (B2v[i] * B0u[j]) * CV;
+ dUV += (B1v[i] * B1u[j]) * CV;
+ }
+ }
+
+ dUU *= 6 * level;
+ dVV *= 6 * level;
+ dUV *= 9 * level;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ float nLengthInv = 1.0;
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0 / nLength;
+ } else {
+ // N may have been resolved above if degenerate, but if N was resolved
+ // we don't have an accurate length for its un-normalized value, and that
+ // length is needed to project the un-normalized dNu and dNv into the
+ // tangent plane of N.
+ //
+ // So compute N more accurately with available second derivatives, i.e.
+ // with a 1st order Taylor approximation to un-normalized N(u,v).
+
+ float DU = (UV.x == 1.0f) ? -1.0f : 1.0f;
+ float DV = (UV.y == 1.0f) ? -1.0f : 1.0f;
+
+ N = DU * dNu + DV * dNv;
+
+ nLength = length(N);
+ if (nLength > nEpsilon) {
+ nLengthInv = 1.0f / nLength;
+ N = N * nLengthInv;
+ }
+ }
+
+ // Project derivatives of non-unit normals into tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) * nLengthInv;
+ dNv = (dNv - dot(dNv,N) * N) * nLengthInv;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// Gregory Basis
+// ----------------------------------------------------------------------------
+
+struct OsdPerPatchVertexGregoryBasis {
+ ivec3 patchParam;
+ vec3 P;
+};
+
+void
+OsdComputePerPatchVertexGregoryBasis(ivec3 patchParam, int ID, vec3 cv,
+ out OsdPerPatchVertexGregoryBasis result)
+{
+ result.patchParam = patchParam;
+ result.P = cv;
+}
+
+void
+OsdEvalPatchGregory(ivec3 patchParam, vec2 UV, vec3 cv[20],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x, v = UV.y;
+ float U = 1-u, V = 1-v;
+
+ //(0,1) (1,1)
+ // 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 f0+ 9 |
+ // | | | f1- |
+ // | | | |
+ // | | | |
+ // 0--------1--------7--------5
+ // P0 e0+ e1- P1
+ //(0,0) (1,0)
+
+ float d11 = u+v;
+ float d12 = U+v;
+ float d21 = u+V;
+ float d22 = U+V;
+
+ OsdPerPatchVertexBezier bezcv[16];
+
+ bezcv[ 5].P = (d11 == 0.0) ? cv[3] : (u*cv[3] + v*cv[4])/d11;
+ bezcv[ 6].P = (d12 == 0.0) ? cv[8] : (U*cv[9] + v*cv[8])/d12;
+ bezcv[ 9].P = (d21 == 0.0) ? cv[18] : (u*cv[19] + V*cv[18])/d21;
+ bezcv[10].P = (d22 == 0.0) ? cv[13] : (U*cv[13] + V*cv[14])/d22;
+
+ bezcv[ 0].P = cv[0];
+ bezcv[ 1].P = cv[1];
+ bezcv[ 2].P = cv[7];
+ bezcv[ 3].P = cv[5];
+ bezcv[ 4].P = cv[2];
+ bezcv[ 7].P = cv[6];
+ bezcv[ 8].P = cv[16];
+ bezcv[11].P = cv[12];
+ bezcv[12].P = cv[15];
+ bezcv[13].P = cv[17];
+ bezcv[14].P = cv[11];
+ bezcv[15].P = cv[10];
+
+ OsdEvalPatchBezier(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+//
+// Convert the 12 points of a regular patch resulting from Loop subdivision
+// into a more accessible Bezier patch for both tessellation assessment and
+// evaluation.
+//
+// Regular patch for Loop subdivision -- quartic triangular Box spline:
+//
+// 10 --- 11
+// . . . .
+// . . . .
+// 7 --- 8 --- 9
+// . . . . . .
+// . . . . . .
+// 3 --- 4 --- 5 --- 6
+// . . . . . .
+// . . . . . .
+// 0 --- 1 --- 2
+//
+// The equivalant quartic Bezier triangle (15 points):
+//
+// 14
+// . .
+// . .
+// 12 --- 13
+// . . . .
+// . . . .
+// 9 -- 10 --- 11
+// . . . . . .
+// . . . . . .
+// 5 --- 6 --- 7 --- 8
+// . . . . . . . .
+// . . . . . . . .
+// 0 --- 1 --- 2 --- 3 --- 4
+//
+// A hybrid cubic/quartic Bezier patch with cubic boundaries is a close
+// approximation and would only use 12 control points, but we need a full
+// quartic patch to maintain accuracy along boundary curves -- especially
+// between subdivision levels.
+//
+void
+OsdComputePerPatchVertexBoxSplineTriangle(ivec3 patchParam, int ID, vec3 cv[12],
+ out OsdPerPatchVertexBezier result)
+{
+ //
+ // Conversion matrix from 12-point Box spline to 15-point quartic Bezier
+ // patch and its common scale factor:
+ //
+ const float boxToBezierMatrix[12*15] = float[12*15](
+ // L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11
+ 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, 0, // B0
+ 1, 3, 0, 0, 12, 4, 0, 1, 3, 0, 0, 0, // B1
+ 0, 4, 0, 0, 8, 8, 0, 0, 4, 0, 0, 0, // B2
+ 0, 3, 1, 0, 4, 12, 0, 0, 3, 1, 0, 0, // B3
+ 0, 2, 2, 0, 2, 12, 2, 0, 2, 2, 0, 0, // B4
+ 0, 1, 0, 1, 12, 3, 0, 3, 4, 0, 0, 0, // B5
+ 0, 1, 0, 0, 10, 6, 0, 1, 6, 0, 0, 0, // B6
+ 0, 1, 0, 0, 6, 10, 0, 0, 6, 1, 0, 0, // B7
+ 0, 1, 0, 0, 3, 12, 1, 0, 4, 3, 0, 0, // B8
+ 0, 0, 0, 0, 8, 4, 0, 4, 8, 0, 0, 0, // B9
+ 0, 0, 0, 0, 6, 6, 0, 1, 10, 1, 0, 0, // B10
+ 0, 0, 0, 0, 4, 8, 0, 0, 8, 4, 0, 0, // B11
+ 0, 0, 0, 0, 4, 3, 0, 3, 12, 1, 1, 0, // B12
+ 0, 0, 0, 0, 3, 4, 0, 1, 12, 3, 0, 1, // B13
+ 0, 0, 0, 0, 2, 2, 0, 2, 12, 2, 2, 2 // B14
+ );
+ const float boxToBezierMatrixScale = 1.0 / 24.0;
+
+ OsdComputeBoxSplineTriangleBoundaryPoints(cv, patchParam);
+
+ result.patchParam = patchParam;
+ result.P = vec3(0);
+
+ int cvCoeffBase = 12 * ID;
+
+ for (int i = 0; i < 12; ++i) {
+ result.P += boxToBezierMatrix[cvCoeffBase + i] * cv[i];
+ }
+ result.P *= boxToBezierMatrixScale;
+}
+
+void
+OsdEvalPatchBezierTriangle(ivec3 patchParam, vec2 UV,
+ OsdPerPatchVertexBezier cv[15],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float uu = u * u;
+ float vv = v * v;
+ float ww = w * w;
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // When computing normal derivatives, we need 2nd derivatives, so compute
+ // an intermediate quadratic Bezier triangle from which 2nd derivatives
+ // can be easily computed, and which in turn yields the triangle that gives
+ // the position and 1st derivatives.
+ //
+ // Quadratic barycentric basis functions (in addition to those above):
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q0 = ww * cv[ 0].P + wu * cv[ 1].P + uu * cv[ 2].P +
+ uv * cv[ 6].P + vv * cv[ 9].P + vw * cv[ 5].P;
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q2 = ww * cv[ 2].P + wu * cv[ 3].P + uu * cv[ 4].P +
+ uv * cv[ 8].P + vv * cv[11].P + vw * cv[ 7].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+ vec3 Q5 = ww * cv[ 9].P + wu * cv[10].P + uu * cv[11].P +
+ uv * cv[13].P + vv * cv[14].P + vw * cv[12].P;
+
+ vec3 V0 = w * Q0 + u * Q1 + v * Q3;
+ vec3 V1 = w * Q1 + u * Q2 + v * Q4;
+ vec3 V2 = w * Q3 + u * Q4 + v * Q5;
+#else
+ //
+ // When 2nd derivatives are not required, factor the recursive evaluation
+ // of a point to directly provide the three points of the triangle at the
+ // last stage -- which then trivially provides both position and 1st
+ // derivatives. Each point of the triangle results from evaluating the
+ // corresponding cubic Bezier sub-triangle for each corner of the quartic:
+ //
+ // Cubic barycentric basis functions:
+ float uuu = uu * u;
+ float uuv = uu * v * 3.0;
+ float uvv = u * vv * 3.0;
+ float vvv = vv * v;
+ float vvw = vv * w * 3.0;
+ float vww = v * ww * 3.0;
+ float www = ww * w;
+ float wwu = ww * u * 3.0;
+ float wuu = w * uu * 3.0;
+ float uvw = u * v * w * 6.0;
+
+ vec3 V0 = www * cv[ 0].P + wwu * cv[ 1].P + wuu * cv[ 2].P
+ + uuu * cv[ 3].P + uuv * cv[ 7].P + uvv * cv[10].P
+ + vvv * cv[12].P + vvw * cv[ 9].P + vww * cv[ 5].P + uvw * cv[ 6].P;
+
+ vec3 V1 = www * cv[ 1].P + wwu * cv[ 2].P + wuu * cv[ 3].P
+ + uuu * cv[ 4].P + uuv * cv[ 8].P + uvv * cv[11].P
+ + vvv * cv[13].P + vvw * cv[10].P + vww * cv[ 6].P + uvw * cv[ 7].P;
+
+ vec3 V2 = www * cv[ 5].P + wwu * cv[ 6].P + wuu * cv[ 7].P
+ + uuu * cv[ 8].P + uuv * cv[11].P + uvv * cv[13].P
+ + vvv * cv[14].P + vvw * cv[12].P + vww * cv[ 9].P + uvw * cv[10].P;
+#endif
+
+ //
+ // Compute P, du and dv all from the triangle formed from the three Vi:
+ //
+ P = w * V0 + u * V1 + v * V2;
+
+ int dSign = OsdGetPatchIsTriangleRotated(patchParam) ? -1 : 1;
+ int level = OsdGetPatchFaceLevel(patchParam);
+
+ float d1Scale = dSign * level * 4;
+
+ dPu = (V1 - V0) * d1Scale;
+ dPv = (V2 - V0) * d1Scale;
+
+ // Compute N and test for degeneracy:
+ //
+ // We need a geometric measure of the size of the patch for a suitable
+ // tolerance. Magnitudes of the partials are generally proportional to
+ // that size -- the sum of the partials is readily available, cheap to
+ // compute, and has proved effective in most cases (though not perfect).
+ // The size of the bounding box of the patch, or some approximation to
+ // it, would be better but more costly to compute.
+ //
+ float proportionalNormalTolerance = 0.00001f;
+
+ float nEpsilon = (length(dPu) + length(dPv)) * proportionalNormalTolerance;
+
+ N = cross(dPu, dPv);
+ float nLength = length(N);
+
+
+#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
+ //
+ // Compute normal derivatives using 2nd order partials, then use the
+ // normal derivatives to resolve a degenerate normal:
+ //
+ float d2Scale = dSign * level * level * 12;
+
+ vec3 dUU = (Q0 - 2 * Q1 + Q2) * d2Scale;
+ vec3 dVV = (Q0 - 2 * Q3 + Q5) * d2Scale;
+ vec3 dUV = (Q0 - Q1 + Q4 - Q3) * d2Scale;
+
+ dNu = cross(dUU, dPv) + cross(dPu, dUV);
+ dNv = cross(dUV, dPv) + cross(dPu, dVV);
+
+ if (nLength < nEpsilon) {
+ // Use 1st order Taylor approximation of N(u,v) within patch interior:
+ if (w > 0.0) {
+ N = dNu + dNv;
+ } else if (u >= 1.0) {
+ N = -dNu + dNv;
+ } else if (v >= 1.0) {
+ N = dNu - dNv;
+ } else {
+ N = -dNu - dNv;
+ }
+
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+
+ // Project derivs of non-unit normal function onto tangent plane of N:
+ dNu = (dNu - dot(dNu,N) * N) / nLength;
+ dNv = (dNv - dot(dNv,N) * N) / nLength;
+#else
+ dNu = vec3(0);
+ dNv = vec3(0);
+
+ //
+ // Resolve a degenerate normal using the interior triangle of the
+ // intermediate quadratic patch that results from recursive evaluation.
+ // This addresses common cases of degenerate or colinear boundaries
+ // without resorting to use of explicit 2nd derivatives:
+ //
+ if (nLength < nEpsilon) {
+ float uv = u * v * 2.0;
+ float vw = v * w * 2.0;
+ float wu = w * u * 2.0;
+
+ vec3 Q1 = ww * cv[ 1].P + wu * cv[ 2].P + uu * cv[ 3].P +
+ uv * cv[ 7].P + vv * cv[10].P + vw * cv[ 6].P;
+ vec3 Q3 = ww * cv[ 5].P + wu * cv[ 6].P + uu * cv[ 7].P +
+ uv * cv[10].P + vv * cv[12].P + vw * cv[ 9].P;
+ vec3 Q4 = ww * cv[ 6].P + wu * cv[ 7].P + uu * cv[ 8].P +
+ uv * cv[11].P + vv * cv[13].P + vw * cv[10].P;
+
+ N = cross((Q4 - Q1), (Q3 - Q1));
+ nLength = length(N);
+ if (nLength < nEpsilon) {
+ nLength = 1.0;
+ }
+ }
+ N = N / nLength;
+#endif
+}
+
+void
+OsdEvalPatchGregoryTriangle(ivec3 patchParam, vec2 UV, vec3 cv[18],
+ out vec3 P, out vec3 dPu, out vec3 dPv,
+ out vec3 N, out vec3 dNu, out vec3 dNv)
+{
+ float u = UV.x;
+ float v = UV.y;
+ float w = 1.0 - u - v;
+
+ float duv = u + v;
+ float dvw = v + w;
+ float dwu = w + u;
+
+ OsdPerPatchVertexBezier bezcv[15];
+
+ bezcv[ 6].P = (duv == 0.0) ? cv[3] : ((u*cv[ 3] + v*cv[ 4]) / duv);
+ bezcv[ 7].P = (dvw == 0.0) ? cv[8] : ((v*cv[ 8] + w*cv[ 9]) / dvw);
+ bezcv[10].P = (dwu == 0.0) ? cv[13] : ((w*cv[13] + u*cv[14]) / dwu);
+
+ bezcv[ 0].P = cv[ 0];
+ bezcv[ 1].P = cv[ 1];
+ bezcv[ 2].P = cv[15];
+ bezcv[ 3].P = cv[ 7];
+ bezcv[ 4].P = cv[ 5];
+ bezcv[ 5].P = cv[ 2];
+ bezcv[ 8].P = cv[ 6];
+ bezcv[ 9].P = cv[17];
+ bezcv[11].P = cv[16];
+ bezcv[12].P = cv[11];
+ bezcv[13].P = cv[12];
+ bezcv[14].P = cv[10];
+
+ OsdEvalPatchBezierTriangle(patchParam, UV, bezcv, P, dPu, dPv, N, dNu, dNv);
+}
+
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Tessellation
+// ----------------------------------------------------------------------------
+
+// For now, fractional spacing is supported only with screen space tessellation
+#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
+#undef OSD_FRACTIONAL_EVEN_SPACING
+#undef OSD_FRACTIONAL_ODD_SPACING
+#endif
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ #define OSD_SPACING fractional_even_spacing
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ #define OSD_SPACING fractional_odd_spacing
+#else
+ #define OSD_SPACING equal_spacing
+#endif
+
+//
+// Organization of B-spline and Bezier control points.
+//
+// Each patch is defined by 16 control points (labeled 0-15).
+//
+// The patch will be evaluated across the domain from (0,0) at
+// the lower-left to (1,1) at the upper-right. When computing
+// adaptive tessellation metrics, we consider refined vertex-vertex
+// and edge-vertex points along the transition edges of the patch
+// (labeled vv* and ev* respectively).
+//
+// The two segments of each transition edge are labeled Lo and Hi,
+// with the Lo segment occurring before the Hi segment along the
+// transition edge's domain parameterization. These Lo and Hi segment
+// tessellation levels determine how domain evaluation coordinates
+// are remapped along transition edges. The Hi segment value will
+// be zero for a non-transition edge.
+//
+// (0,1) (1,1)
+//
+// vv3 ev23 vv2
+// | Lo3 | Hi3 |
+// --O-----------O-----+-----O-----------O--
+// | 12 | 13 14 | 15 |
+// | | | |
+// | | | |
+// Hi0 | | | | Hi2
+// | | | |
+// O-----------O-----------O-----------O
+// | 8 | 9 10 | 11 |
+// | | | |
+// ev03 --+ | | +-- ev12
+// | | | |
+// | 4 | 5 6 | 7 |
+// O-----------O-----------O-----------O
+// | | | |
+// Lo0 | | | | Lo2
+// | | | |
+// | | | |
+// | 0 | 1 2 | 3 |
+// --O-----------O-----+-----O-----------O--
+// | Lo1 | Hi1 |
+// vv0 ev01 vv1
+//
+// (0,0) (1,0)
+//
+
+#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
+
+float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
+{
+ vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
+ return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
+}
+
+float OsdComputeTessLevel(vec3 p0, vec3 p1)
+{
+ // Adaptive factor can be any computation that depends only on arg values.
+ // Project the diameter of the edge's bounding sphere instead of using the
+ // length of the projected edge itself to avoid problems near silhouettes.
+ p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
+ p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
+ vec3 center = (p0 + p1) / 2.0;
+ float diameter = distance(p0, p1);
+ float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
+ float tessLevel = max(1.0, OsdTessLevel() * projLength);
+
+ // We restrict adaptive tessellation levels to half of the device
+ // supported maximum because transition edges are split into two
+ // halves and the sum of the two corresponding levels must not exceed
+ // the device maximum. We impose this limit even for non-transition
+ // edges because a non-transition edge must be able to match up with
+ // one half of the transition edge of an adjacent transition patch.
+ return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ ((transitionMask & 4) >> 2));
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Uniform factors are simple powers of two for each level.
+ // The maximum here can be increased if we know the maximum
+ // refinement level of the mesh:
+ // min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
+ int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
+ float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
+ pow(2, refinementLevel-1);
+
+ // tessLevels of transition edge should be clamped to 2.
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+ vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
+ ((transitionMask & 1) >> 0),
+ ((transitionMask & 2) >> 1),
+ 0);
+
+ tessOuterLo = max(vec4(tessLevel), tessLevelMin);
+ tessOuterHi = vec4(0);
+}
+
+void
+OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. We compute the corresponding
+ // vertex-vertex and edge-vertex refined points along the edges of the
+ // patch using Catmull-Clark subdivision stencil weights.
+ // For simplicity, we let the optimizer discard unused computation.
+
+ vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
+ (cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
+ vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
+ (cp[5] + cp[6]) * 0.375;
+
+ vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
+ (cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
+ vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
+ (cp[6] + cp[10]) * 0.375;
+
+ vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
+ (cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
+ vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
+ (cp[9] + cp[10]) * 0.375;
+
+ vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
+ (cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
+ vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
+ (cp[5] + cp[9]) * 0.375;
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
+ tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
+ tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
+ tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
+ tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
+ }
+}
+
+//
+// Patch boundary corners are ordered counter-clockwise from the first
+// corner while patch boundary edges and their midpoints are similarly
+// ordered counter-clockwise beginning at the edge preceding corner[0].
+//
+void
+Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 8) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
+ tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
+ } else {
+ tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
+ }
+}
+
+void
+Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ if ((transitionMask & 4) != 0) {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
+ tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
+ } else {
+ tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
+ }
+ if ((transitionMask & 1) != 0) {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
+ tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
+ } else {
+ tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
+ }
+ if ((transitionMask & 2) != 0) {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
+ tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
+ } else {
+ tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
+ }
+}
+
+vec3
+Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
+{
+ // Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
+ return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
+}
+
+vec3
+Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
+{
+ // Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
+ return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the four corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ vec3 corners[4];
+ vec3 midpoints[4];
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
+ corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
+ corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
+ corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
+#else
+ corners[0] = cpBezier[ 0].P;
+ corners[1] = cpBezier[ 3].P;
+ corners[2] = cpBezier[15].P;
+ corners[3] = cpBezier[12].P;
+
+ midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
+ midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalBezierCurveMidPoint(
+ cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
+#endif
+
+ Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ // Each edge of a transition patch is adjacent to one or two patches
+ // at the next refined level of subdivision. When the patch control
+ // points have been converted to the Bezier basis, the control points
+ // at the corners are on the limit surface (since a Bezier patch
+ // interpolates its corner control points). We can compute an adaptive
+ // tessellation level for transition edges on the limit surface by
+ // evaluating a limit position at the mid point of each transition edge.
+
+ tessOuterLo = vec4(0);
+ tessOuterHi = vec4(0);
+
+ int transitionMask = OsdGetPatchTransitionMask(patchParam);
+
+ vec3 corners[3];
+ corners[0] = cv[0];
+ corners[1] = cv[4];
+ corners[2] = cv[14];
+
+ vec3 midpoints[3];
+ midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
+ midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
+ midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
+ Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
+
+ Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
+ patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Round up to the nearest even integer
+float OsdRoundUpEven(float x) {
+ return 2*ceil(x/2);
+}
+
+// Round up to the nearest odd integer
+float OsdRoundUpOdd(float x) {
+ return 2*ceil((x+1)/2)-1;
+}
+
+// Compute outer and inner tessellation levels taking into account the
+// current tessellation spacing mode.
+void
+OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ // Outer levels are the sum of the Lo and Hi segments where the Hi
+ // segments will have lengths of zero for non-transition edges.
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ // Combine fractional outer transition edge levels before rounding.
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+
+ // Round the segments of transition edges separately. We will recover the
+ // fractional parameterization of transition edges after tessellation.
+ //
+ // The sum of the two outer odd segment lengths will be an even number
+ // which the tessellator will increase by +1 so that there will be a
+ // total odd number of segments. We clamp the combinedOuter tess levels
+ // (used to compute the inner tess levels) so that the outer transition
+ // edges will be sampled without degenerate triangles.
+
+ tessLevelOuter = combinedOuter;
+ if (tessOuterHi[0] > 0) {
+ tessLevelOuter[0] =
+ OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[1] > 0) {
+ tessLevelOuter[1] =
+ OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[2] > 0) {
+ tessLevelOuter[2] =
+ OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+ if (tessOuterHi[3] > 0) {
+ tessLevelOuter[3] =
+ OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
+ combinedOuter = max(vec4(3), combinedOuter);
+ }
+#else
+ // Round equally spaced transition edge levels before combining.
+ tessOuterLo = round(tessOuterLo);
+ tessOuterHi = round(tessOuterHi);
+
+ vec4 combinedOuter = tessOuterLo + tessOuterHi;
+ tessLevelOuter = combinedOuter;
+#endif
+
+ // Inner levels are the averages the corresponding outer levels.
+ tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
+ tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
+}
+
+void
+OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+
+ // Inner level is the max of the three outer levels.
+ tessLevelInner[0] = max(max(tessLevelOuter[0],
+ tessLevelOuter[1]),
+ tessLevelOuter[2]);
+}
+
+void
+OsdGetTessLevelsUniform(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+void
+OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner,
+ out vec4 tessOuterLo, out vec4 tessOuterHi)
+{
+ OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
+ tessOuterLo, tessOuterHi);
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+// Deprecated -- prefer use of newer Bezier patch equivalent:
+void
+OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
+ ivec3 patchParam,
+ out vec4 tessLevelOuter, out vec2 tessLevelInner)
+{
+ vec4 tessOuterLo = vec4(0);
+ vec4 tessOuterHi = vec4(0);
+
+#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
+ tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
+ tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
+ tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
+ tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
+ tessOuterHi = vec4(0);
+#else
+ OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
+#endif
+
+ OsdComputeTessLevels(tessOuterLo, tessOuterHi,
+ tessLevelOuter, tessLevelInner);
+}
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
+float
+OsdGetTessFractionalSplit(float t, float level, float levelUp)
+{
+ // Fractional tessellation of an edge will produce n segments where n
+ // is the tessellation level of the edge (level) rounded up to the
+ // nearest even or odd integer (levelUp). There will be n-2 segments of
+ // equal length (dx1) and two additional segments of equal length (dx0)
+ // that are typically shorter than the other segments. The two additional
+ // segments should be placed symmetrically on opposite sides of the
+ // edge (offset).
+
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ if (level <= 2) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
+
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ if (level <= 1) return t;
+
+ float base = pow(2.0,floor(log2(levelUp)));
+ float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
+#endif
+
+ float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
+ float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
+
+ if (t < 0.5) {
+ float x = levelUp/2 - round(t*levelUp);
+ return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else if (t > 0.5) {
+ float x = round(t*levelUp) - levelUp/2;
+ return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
+ } else {
+ return t;
+ }
+}
+#endif
+
+float
+OsdGetTessTransitionSplit(float t, float lo, float hi)
+{
+#if defined OSD_FRACTIONAL_EVEN_SPACING
+ float loRoundUp = OsdRoundUpEven(lo);
+ float hiRoundUp = OsdRoundUpEven(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (loRoundUp + hiRoundUp));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else {
+ float t1 = (ti - loRoundUp) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ }
+#elif defined OSD_FRACTIONAL_ODD_SPACING
+ float loRoundUp = OsdRoundUpOdd(lo);
+ float hiRoundUp = OsdRoundUpOdd(hi);
+
+ // Convert the parametric t into a segment index along the combined edge.
+ // The +1 below is to account for the extra segment produced by the
+ // tessellator since the sum of two odd tess levels will be rounded
+ // up by one to the next odd integer tess level.
+ float ti = round(t * (loRoundUp + hiRoundUp + 1));
+
+ if (ti <= loRoundUp) {
+ float t0 = ti / loRoundUp;
+ return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
+ } else if (ti > (loRoundUp+1)) {
+ float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
+ return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
+ } else {
+ return 0.5;
+ }
+#else
+ // Convert the parametric t into a segment index along the combined edge.
+ float ti = round(t * (lo + hi));
+
+ if (ti <= lo) {
+ return (ti / lo) * 0.5;
+ } else {
+ return ((ti - lo) / hi) * 0.5 + 0.5;
+ }
+#endif
+}
+
+vec2
+OsdGetTessParameterization(vec2 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.x == 1 && tessOuterHi[2] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
+ } else
+ if (p.y == 1 && tessOuterHi[3] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
+ }
+ return UV;
+}
+
+vec2
+OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
+{
+ vec2 UV = p.xy;
+ if (p.x == 0 && tessOuterHi[0] > 0) {
+ UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
+ } else
+ if (p.y == 0 && tessOuterHi[1] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
+ } else
+ if (p.z == 0 && tessOuterHi[2] > 0) {
+ UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
+ UV.y = 1.0 - UV.x;
+ }
+ return UV;
+}
+
+//
+// Copyright 2013-2018 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.
+//
+
+// ----------------------------------------------------------------------------
+// Legacy Gregory
+// ----------------------------------------------------------------------------
+#if defined(OSD_PATCH_GREGORY) || defined(OSD_PATCH_GREGORY_BOUNDARY)
+
+#define M_PI 3.14159265359f
+
+// precomputed catmark coefficient table up to valence 29
+uniform float OsdCatmarkCoefficient[30] = float[](
+ 0, 0, 0, 0.812816, 0.500000, 0.363644, 0.287514,
+ 0.238688, 0.204544, 0.179229, 0.159657,
+ 0.144042, 0.131276, 0.120632, 0.111614,
+ 0.103872, 0.09715, 0.0912559, 0.0860444,
+ 0.0814022, 0.0772401, 0.0734867, 0.0700842,
+ 0.0669851, 0.0641504, 0.0615475, 0.0591488,
+ 0.0569311, 0.0548745, 0.0529621
+ );
+
+float
+OsdComputeCatmarkCoefficient(int valence)
+{
+#if OSD_MAX_VALENCE < 30
+ return OsdCatmarkCoefficient[valence];
+#else
+ if (valence < 30) {
+ return OsdCatmarkCoefficient[valence];
+ } else {
+ float t = 2.0f * float(M_PI) / float(valence);
+ return 1.0f / (valence * (cos(t) + 5.0f +
+ sqrt((cos(t) + 9) * (cos(t) + 1)))/16.0f);
+ }
+#endif
+}
+
+float cosfn(int n, int j) {
+ return cos((2.0f * M_PI * j)/float(n));
+}
+
+float sinfn(int n, int j) {
+ return sin((2.0f * M_PI * j)/float(n));
+}
+
+#if !defined OSD_MAX_VALENCE || OSD_MAX_VALENCE < 1
+#undef OSD_MAX_VALENCE
+#define OSD_MAX_VALENCE 4
+#endif
+
+struct OsdPerVertexGregory {
+ vec3 P;
+ ivec3 clipFlag;
+ int valence;
+ vec3 e0;
+ vec3 e1;
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ int zerothNeighbor;
+ vec3 org;
+#endif
+ vec3 r[OSD_MAX_VALENCE];
+};
+
+struct OsdPerPatchVertexGregory {
+ ivec3 patchParam;
+ vec3 P;
+ vec3 Ep;
+ vec3 Em;
+ vec3 Fp;
+ vec3 Fm;
+};
+
+#ifndef OSD_NUM_ELEMENTS
+#define OSD_NUM_ELEMENTS 3
+#endif
+
+uniform samplerBuffer OsdVertexBuffer;
+uniform isamplerBuffer OsdValenceBuffer;
+
+vec3 OsdReadVertex(int vertexIndex)
+{
+ int index = int(OSD_NUM_ELEMENTS * (vertexIndex + OsdBaseVertex()));
+ return vec3(texelFetch(OsdVertexBuffer, index).x,
+ texelFetch(OsdVertexBuffer, index+1).x,
+ texelFetch(OsdVertexBuffer, index+2).x);
+}
+
+int OsdReadVertexValence(int vertexID)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1));
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+int OsdReadVertexIndex(int vertexID, int valenceVertex)
+{
+ int index = int(vertexID * (2 * OSD_MAX_VALENCE + 1) + 1 + valenceVertex);
+ return texelFetch(OsdValenceBuffer, index).x;
+}
+
+uniform isamplerBuffer OsdQuadOffsetBuffer;
+
+int OsdReadQuadOffset(int primitiveID, int offsetVertex)
+{
+ int index = int(4*primitiveID+OsdGregoryQuadOffsetBase() + offsetVertex);
+ return texelFetch(OsdQuadOffsetBuffer, index).x;
+}
+
+void
+OsdComputePerVertexGregory(int vID, vec3 P, out OsdPerVertexGregory v)
+{
+ v.clipFlag = ivec3(0);
+
+ int ivalence = OsdReadVertexValence(vID);
+ v.valence = ivalence;
+ int valence = abs(ivalence);
+
+ vec3 f[OSD_MAX_VALENCE];
+ vec3 pos = P;
+ vec3 opos = vec3(0);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.org = pos;
+ int boundaryEdgeNeighbors[2];
+ int currNeighbor = 0;
+ int ibefore = 0;
+ int zerothNeighbor = 0;
+#endif
+
+ for (int i=0; i<valence; ++i) {
+ int im = (i+valence-1)%valence;
+ int ip = (i+1)%valence;
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*i);
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ bool isBoundaryNeighbor = false;
+ int valenceNeighbor = OsdReadVertexValence(idx_neighbor);
+
+ if (valenceNeighbor < 0) {
+ isBoundaryNeighbor = true;
+ if (currNeighbor<2) {
+ boundaryEdgeNeighbors[currNeighbor] = idx_neighbor;
+ }
+ currNeighbor++;
+ if (currNeighbor == 1) {
+ ibefore = i;
+ zerothNeighbor = i;
+ } else {
+ if (i-ibefore == 1) {
+ int tmp = boundaryEdgeNeighbors[0];
+ boundaryEdgeNeighbors[0] = boundaryEdgeNeighbors[1];
+ boundaryEdgeNeighbors[1] = tmp;
+ zerothNeighbor = i;
+ }
+ }
+ }
+#endif
+
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*i + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ int idx_neighbor_p = OsdReadVertexIndex(vID, 2*ip);
+ vec3 neighbor_p = OsdReadVertex(idx_neighbor_p);
+
+ int idx_neighbor_m = OsdReadVertexIndex(vID, 2*im);
+ vec3 neighbor_m = OsdReadVertex(idx_neighbor_m);
+
+ int idx_diagonal_m = OsdReadVertexIndex(vID, 2*im + 1);
+ vec3 diagonal_m = OsdReadVertex(idx_diagonal_m);
+
+ f[i] = (pos * float(valence) + (neighbor_p + neighbor)*2.0f + diagonal) / (float(valence)+5.0f);
+
+ opos += f[i];
+ v.r[i] = (neighbor_p-neighbor_m)/3.0f + (diagonal - diagonal_m)/6.0f;
+ }
+
+ opos /= valence;
+ v.P = vec4(opos, 1.0f).xyz;
+
+ vec3 e;
+ v.e0 = vec3(0);
+ v.e1 = vec3(0);
+
+ for(int i=0; i<valence; ++i) {
+ int im = (i + valence -1) % valence;
+ e = 0.5f * (f[i] + f[im]);
+ v.e0 += cosfn(valence, i)*e;
+ v.e1 += sinfn(valence, i)*e;
+ }
+ float ef = OsdComputeCatmarkCoefficient(valence);
+ v.e0 *= ef;
+ v.e1 *= ef;
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ v.zerothNeighbor = zerothNeighbor;
+ if (currNeighbor == 1) {
+ boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
+ }
+
+ if (ivalence < 0) {
+ if (valence > 2) {
+ v.P = (OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ 4.0f * pos)/6.0f;
+ } else {
+ v.P = pos;
+ }
+
+ v.e0 = (OsdReadVertex(boundaryEdgeNeighbors[0]) -
+ OsdReadVertex(boundaryEdgeNeighbors[1]))/6.0;
+
+ float k = float(float(valence) - 1.0f); //k is the number of faces
+ float c = cos(M_PI/k);
+ float s = sin(M_PI/k);
+ float gamma = -(4.0f*s)/(3.0f*k+c);
+ float alpha_0k = -((1.0f+2.0f*c)*sqrt(1.0f+c))/((3.0f*k+c)*sqrt(1.0f-c));
+ float beta_0 = s/(3.0f*k + c);
+
+ int idx_diagonal = OsdReadVertexIndex(vID, 2*zerothNeighbor + 1);
+ vec3 diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 = gamma * pos +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[0]) +
+ alpha_0k * OsdReadVertex(boundaryEdgeNeighbors[1]) +
+ beta_0 * diagonal;
+
+ for (int x=1; x<valence - 1; ++x) {
+ int curri = ((x + zerothNeighbor)%valence);
+ float alpha = (4.0f*sin((M_PI * float(x))/k))/(3.0f*k+c);
+ float beta = (sin((M_PI * float(x))/k) + sin((M_PI * float(x+1))/k))/(3.0f*k+c);
+
+ int idx_neighbor = OsdReadVertexIndex(vID, 2*curri);
+ vec3 neighbor = OsdReadVertex(idx_neighbor);
+
+ idx_diagonal = OsdReadVertexIndex(vID, 2*curri + 1);
+ diagonal = OsdReadVertex(idx_diagonal);
+
+ v.e1 += alpha * neighbor + beta * diagonal;
+ }
+
+ v.e1 /= 3.0f;
+ }
+#endif
+}
+
+void
+OsdComputePerPatchVertexGregory(ivec3 patchParam, int ID, int primitiveID,
+ in OsdPerVertexGregory v[4],
+ out OsdPerPatchVertexGregory result)
+{
+ result.patchParam = patchParam;
+ result.P = v[ID].P;
+
+ int i = ID;
+ int ip = (i+1)%4;
+ int im = (i+3)%4;
+ int valence = abs(v[i].valence);
+ int n = valence;
+
+ int start = OsdReadQuadOffset(primitiveID, i) & 0xff;
+ int prev = (OsdReadQuadOffset(primitiveID, i) >> 8) & 0xff;
+
+ int start_m = OsdReadQuadOffset(primitiveID, im) & 0xff;
+ int prev_p = (OsdReadQuadOffset(primitiveID, ip) >> 8) & 0xff;
+
+ int np = abs(v[ip].valence);
+ int nm = abs(v[im].valence);
+
+ // Control Vertices based on :
+ // "Approximating Subdivision Surfaces with Gregory Patches
+ // for Hardware Tessellation"
+ // Loop, Schaefer, Ni, Castano (ACM ToG Siggraph Asia 2009)
+ //
+ // P3 e3- e2+ P2
+ // O--------O--------O--------O
+ // | | | |
+ // | | | |
+ // | | f3- | f2+ |
+ // | O O |
+ // e3+ O------O O------O e2-
+ // | f3+ f2- |
+ // | |
+ // | |
+ // | f0- f1+ |
+ // e0- O------O O------O e1+
+ // | O O |
+ // | | f0+ | f1- |
+ // | | | |
+ // | | | |
+ // O--------O--------O--------O
+ // P0 e0+ e1- P1
+ //
+
+#ifdef OSD_PATCH_GREGORY_BOUNDARY
+ vec3 Em_ip;
+ if (v[ip].valence < -2) {
+ int j = (np + prev_p - v[ip].zerothNeighbor) % np;
+ Em_ip = v[ip].P + cos((M_PI*j)/float(np-1))*v[ip].e0 + sin((M_PI*j)/float(np-1))*v[ip].e1;
+ } else {
+ Em_ip = v[ip].P + v[ip].e0*cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ }
+
+ vec3 Ep_im;
+ if (v[im].valence < -2) {
+ int j = (nm + start_m - v[im].zerothNeighbor) % nm;
+ Ep_im = v[im].P + cos((M_PI*j)/float(nm-1))*v[im].e0 + sin((M_PI*j)/float(nm-1))*v[im].e1;
+ } else {
+ Ep_im = v[im].P + v[im].e0*cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+ }
+
+ if (v[i].valence < 0) {
+ n = (n-1)*2;
+ }
+ if (v[im].valence < 0) {
+ nm = (nm-1)*2;
+ }
+ if (v[ip].valence < 0) {
+ np = (np-1)*2;
+ }
+
+ if (v[i].valence > 2) {
+ result.Ep = v[i].P + v[i].e0*cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0*cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ float s1=3-2*cosfn(n,1)-cosfn(np,1);
+ float s2=2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ } else if (v[i].valence < -2) {
+ int j = (valence + start - v[i].zerothNeighbor) % valence;
+
+ result.Ep = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+ j = (valence + prev - v[i].zerothNeighbor) % valence;
+ result.Em = v[i].P + cos((M_PI*j)/float(valence-1))*v[i].e0 + sin((M_PI*j)/float(valence-1))*v[i].e1;
+
+ vec3 Rp = ((-2.0f * v[i].org - 1.0f * v[im].org) + (2.0f * v[ip].org + 1.0f * v[(i+2)%4].org))/3.0f;
+ vec3 Rm = ((-2.0f * v[i].org - 1.0f * v[ip].org) + (2.0f * v[im].org + 1.0f * v[(i+2)%4].org))/3.0f;
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+
+ if (v[im].valence < 0) {
+ s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ result.Fp = result.Fm = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ } else if (v[ip].valence < 0) {
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/n)-cos(2.0f*M_PI/nm);
+ result.Fm = result.Fp = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+ }
+
+ } else if (v[i].valence == -2) {
+ result.Ep = (2.0f * v[i].org + v[ip].org)/3.0f;
+ result.Em = (2.0f * v[i].org + v[im].org)/3.0f;
+ result.Fp = result.Fm = (4.0f * v[i].org + v[(i+2)%n].org + 2.0f * v[ip].org + 2.0f * v[im].org)/9.0f;
+ }
+
+#else // not OSD_PATCH_GREGORY_BOUNDARY
+
+ result.Ep = v[i].P + v[i].e0 * cosfn(n, start) + v[i].e1*sinfn(n, start);
+ result.Em = v[i].P + v[i].e0 * cosfn(n, prev ) + v[i].e1*sinfn(n, prev);
+
+ vec3 Em_ip = v[ip].P + v[ip].e0 * cosfn(np, prev_p ) + v[ip].e1*sinfn(np, prev_p);
+ vec3 Ep_im = v[im].P + v[im].e0 * cosfn(nm, start_m) + v[im].e1*sinfn(nm, start_m);
+
+ float s1 = 3-2*cosfn(n,1)-cosfn(np,1);
+ float s2 = 2*cosfn(n,1);
+
+ result.Fp = (cosfn(np,1)*v[i].P + s1*result.Ep + s2*Em_ip + v[i].r[start])/3.0f;
+ s1 = 3.0f-2.0f*cos(2.0f*M_PI/float(n))-cos(2.0f*M_PI/float(nm));
+ result.Fm = (cosfn(nm,1)*v[i].P + s1*result.Em + s2*Ep_im - v[i].r[prev])/3.0f;
+#endif
+}
+
+#endif // OSD_PATCH_GREGORY || OSD_PATCH_GREGORY_BOUNDARY
+
+// ----------------------------------------------------------------------------
+// Legacy Face-varying
+// ----------------------------------------------------------------------------
+uniform samplerBuffer OsdFVarDataBuffer;
+
+#ifndef OSD_FVAR_WIDTH
+#define OSD_FVAR_WIDTH 0
+#endif
+
+// ------ extract from quads (catmark, bilinear) ---------
+// XXX: only linear interpolation is supported
+
+#define OSD_COMPUTE_FACE_VARYING_1(result, fvarOffset, tessCoord) { float v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = texelFetch(OsdFVarDataBuffer, index).s } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_2(result, fvarOffset, tessCoord) { vec2 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_3(result, fvarOffset, tessCoord) { vec3 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+#define OSD_COMPUTE_FACE_VARYING_4(result, fvarOffset, tessCoord) { vec4 v[4]; int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 4; for (int i = 0; i < 4; ++i) { int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset; v[i] = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); } result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); }
+
+// ------ extract from triangles barycentric (loop) ---------
+// XXX: no interpolation supported
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_1(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = texelFetch(OsdFVarDataBuffer, index).s; }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_2(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec2(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_3(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec3(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s); }
+
+#define OSD_COMPUTE_FACE_VARYING_TRI_4(result, fvarOffset, triVert) { int primOffset = OsdGetPatchIndex(gl_PrimitiveID) * 3; int index = (primOffset+triVert)*OSD_FVAR_WIDTH + fvarOffset; result = vec4(texelFetch(OsdFVarDataBuffer, index).s, texelFetch(OsdFVarDataBuffer, index + 1).s, texelFetch(OsdFVarDataBuffer, index + 2).s, texelFetch(OsdFVarDataBuffer, index + 3).s); }
+
+
+#define FRAGMENT_SHADER
+//
+// 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.
+//
+
+#if defined(SHADING_VARYING_COLOR) || defined(SHADING_FACEVARYING_COLOR)
+#undef OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_DECLARE vec3 color;
+
+#undef OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE layout(location = 1) in vec3 color;
+
+#undef OSD_USER_VARYING_PER_VERTEX
+#define OSD_USER_VARYING_PER_VERTEX() outpt.color = color
+
+#undef OSD_USER_VARYING_PER_CONTROL_POINT
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN) outpt[ID_OUT].color = inpt[ID_IN].color
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d) outpt.color = mix(mix(inpt[a].color, inpt[b].color, UV.x), mix(inpt[c].color, inpt[d].color, UV.x), UV.y)
+
+#undef OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c) outpt.color = inpt[a].color * (1.0f - UV.x - UV.y) + inpt[b].color * UV.x + inpt[c].color * UV.y;
+#else
+#define OSD_USER_VARYING_DECLARE
+#define OSD_USER_VARYING_ATTRIBUTE_DECLARE
+#define OSD_USER_VARYING_PER_VERTEX()
+#define OSD_USER_VARYING_PER_CONTROL_POINT(ID_OUT, ID_IN)
+#define OSD_USER_VARYING_PER_EVAL_POINT(UV, a, b, c, d)
+#define OSD_USER_VARYING_PER_EVAL_POINT_TRIANGLE(UV, a, b, c)
+#endif
+
+//--------------------------------------------------------------
+// Uniforms / Uniform Blocks
+//--------------------------------------------------------------
+
+layout(std140) uniform Transform {
+ mat4 ModelViewMatrix;
+ mat4 ProjectionMatrix;
+ mat4 ModelViewProjectionMatrix;
+ mat4 ModelViewInverseMatrix;
+};
+
+layout(std140) uniform Tessellation {
+ float TessLevel;
+};
+
+uniform int GregoryQuadOffsetBase;
+uniform int PrimitiveIdBase;
+
+//--------------------------------------------------------------
+// Osd external functions
+//--------------------------------------------------------------
+
+mat4 OsdModelViewMatrix()
+{
+ return ModelViewMatrix;
+}
+mat4 OsdProjectionMatrix()
+{
+ return ProjectionMatrix;
+}
+mat4 OsdModelViewProjectionMatrix()
+{
+ return ModelViewProjectionMatrix;
+}
+float OsdTessLevel()
+{
+ return TessLevel;
+}
+int OsdGregoryQuadOffsetBase()
+{
+ return GregoryQuadOffsetBase;
+}
+int OsdPrimitiveIdBase()
+{
+ return PrimitiveIdBase;
+}
+int OsdBaseVertex()
+{
+ return 0;
+}
+
+//--------------------------------------------------------------
+// Vertex Shader
+//--------------------------------------------------------------
+#ifdef VERTEX_SHADER
+
+layout (location=0) in vec4 position;
+OSD_USER_VARYING_ATTRIBUTE_DECLARE
+
+out block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+void main()
+{
+ outpt.v.position = ModelViewMatrix * position;
+ outpt.v.patchCoord = vec4(0);
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = vec2(0);
+#endif
+ OSD_USER_VARYING_PER_VERTEX();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Geometry Shader
+//--------------------------------------------------------------
+#ifdef GEOMETRY_SHADER
+
+#ifdef PRIM_QUAD
+
+ layout(lines_adjacency) in;
+
+ #define EDGE_VERTS 4
+
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+
+ layout(triangles) in;
+
+ #define EDGE_VERTS 3
+
+#endif // PRIM_TRI
+
+
+layout(triangle_strip, max_vertices = EDGE_VERTS) out;
+in block {
+ OutputVertex v;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt[EDGE_VERTS];
+
+out block {
+ OutputVertex v;
+ noperspective out vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} outpt;
+
+uniform isamplerBuffer OsdFVarParamBuffer;
+layout(std140) uniform OsdFVarArrayData {
+ OsdPatchArray fvarPatchArray[2];
+};
+
+vec2
+interpolateFaceVarying(vec2 uv, int fvarOffset)
+{
+ int patchIndex = OsdGetPatchIndex(gl_PrimitiveID);
+
+ OsdPatchArray array = fvarPatchArray[0];
+
+ ivec3 fvarPatchParam = texelFetch(OsdFVarParamBuffer, patchIndex).xyz;
+ OsdPatchParam param = OsdPatchParamInit(fvarPatchParam.x,
+ fvarPatchParam.y,
+ fvarPatchParam.z);
+
+ int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
+
+ float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
+ int numPoints = OsdEvaluatePatchBasisNormalized(patchType, param,
+ uv.s, uv.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
+
+ int patchArrayStride = numPoints;
+
+ int primOffset = patchIndex * patchArrayStride;
+
+ vec2 result = vec2(0);
+ for (int i=0; i<numPoints; ++i) {
+ int index = (primOffset+i)*OSD_FVAR_WIDTH + fvarOffset;
+ vec2 cv = vec2(texelFetch(OsdFVarDataBuffer, index).s,
+ texelFetch(OsdFVarDataBuffer, index + 1).s);
+ result += wP[i] * cv;
+ }
+
+ return result;
+}
+
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.patchCoord = inpt[index].v.patchCoord;
+#ifdef SMOOTH_NORMALS
+ outpt.v.normal = inpt[index].v.normal;
+#else
+ outpt.v.normal = normal;
+#endif
+
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ outpt.vSegments = inpt[index].vSegments;
+#endif
+
+#ifdef SHADING_VARYING_COLOR
+ outpt.color = inpt[index].color;
+#endif
+
+#ifdef SHADING_FACEVARYING_COLOR
+#ifdef SHADING_FACEVARYING_UNIFORM_SUBDIVISION
+ // interpolate fvar data at refined tri or quad vertex locations
+#ifdef PRIM_TRI
+ vec2 trist[3] = vec2[](vec2(0,0), vec2(1,0), vec2(0,1));
+ vec2 st = trist[index];
+#endif
+#ifdef PRIM_QUAD
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+#endif
+#else
+ // interpolate fvar data at tessellated vertex locations
+ vec2 st = inpt[index].v.tessCoord;
+#endif
+
+ vec2 uv = interpolateFaceVarying(st, /*fvarOffset*/0);
+ outpt.color = vec3(uv.s, uv.t, 0);
+#endif
+
+ gl_Position = ProjectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+const float VIEWPORT_SCALE = 1024.0; // XXXdyu
+
+float edgeDistance(vec4 p, vec4 p0, vec4 p1)
+{
+ return VIEWPORT_SCALE *
+ abs((p.x - p0.x) * (p1.y - p0.y) -
+ (p.y - p0.y) * (p1.x - p0.x)) / length(p1.xy - p0.xy);
+}
+
+void emit(int index, vec3 normal, vec4 edgeVerts[EDGE_VERTS])
+{
+ outpt.edgeDistance[0] =
+ edgeDistance(edgeVerts[index], edgeVerts[0], edgeVerts[1]);
+ outpt.edgeDistance[1] =
+ edgeDistance(edgeVerts[index], edgeVerts[1], edgeVerts[2]);
+#ifdef PRIM_TRI
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[0]);
+#endif
+#ifdef PRIM_QUAD
+ outpt.edgeDistance[2] =
+ edgeDistance(edgeVerts[index], edgeVerts[2], edgeVerts[3]);
+ outpt.edgeDistance[3] =
+ edgeDistance(edgeVerts[index], edgeVerts[3], edgeVerts[0]);
+#endif
+
+ emit(index, normal);
+}
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef PRIM_QUAD
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 C = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+ edgeVerts[3] = ProjectionMatrix * inpt[3].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+ edgeVerts[3].xy /= edgeVerts[3].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(3, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(3, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_QUAD
+
+#ifdef PRIM_TRI
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[2].v.position - inpt[1].v.position).xyz;
+ vec3 n0 = normalize(cross(B, A));
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ vec4 edgeVerts[EDGE_VERTS];
+ edgeVerts[0] = ProjectionMatrix * inpt[0].v.position;
+ edgeVerts[1] = ProjectionMatrix * inpt[1].v.position;
+ edgeVerts[2] = ProjectionMatrix * inpt[2].v.position;
+
+ edgeVerts[0].xy /= edgeVerts[0].w;
+ edgeVerts[1].xy /= edgeVerts[1].w;
+ edgeVerts[2].xy /= edgeVerts[2].w;
+
+ emit(0, n0, edgeVerts);
+ emit(1, n0, edgeVerts);
+ emit(2, n0, edgeVerts);
+#else
+ emit(0, n0);
+ emit(1, n0);
+ emit(2, n0);
+#endif
+#endif // PRIM_TRI
+
+ EndPrimitive();
+}
+
+#endif
+
+//--------------------------------------------------------------
+// Fragment Shader
+//--------------------------------------------------------------
+#ifdef FRAGMENT_SHADER
+
+in block {
+ OutputVertex v;
+ noperspective in vec4 edgeDistance;
+#ifdef OSD_PATCH_ENABLE_SINGLE_CREASE
+ vec2 vSegments;
+#endif
+ OSD_USER_VARYING_DECLARE
+} inpt;
+
+out vec4 outColor;
+
+#define NUM_LIGHTS 2
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[NUM_LIGHTS];
+};
+
+uniform vec4 diffuseColor = vec4(1);
+uniform vec4 ambientColor = vec4(1);
+
+vec4
+lighting(vec4 diffuse, vec3 Peye, vec3 Neye)
+{
+ vec4 color = vec4(0);
+
+ for (int i = 0; i < NUM_LIGHTS; ++i) {
+
+ vec4 Plight = lightSource[i].position;
+
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz) : normalize(Plight.xyz - Peye);
+
+ vec3 n = normalize(Neye);
+ vec3 h = normalize(l + vec3(0,0,1)); // directional viewer
+
+ float d = max(0.0, dot(n, l));
+ float s = pow(max(0.0, dot(n, h)), 500.0f);
+
+ color += lightSource[i].ambient * ambientColor
+ + d * lightSource[i].diffuse * diffuse
+ + s * lightSource[i].specular;
+ }
+
+ color.a = 1;
+ return color;
+}
+
+vec4
+edgeColor(vec4 Cfill, vec4 edgeDistance)
+{
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+#ifdef PRIM_TRI
+ float d =
+ min(inpt.edgeDistance[0], min(inpt.edgeDistance[1], inpt.edgeDistance[2]));
+#endif
+#ifdef PRIM_QUAD
+ float d =
+ min(min(inpt.edgeDistance[0], inpt.edgeDistance[1]),
+ min(inpt.edgeDistance[2], inpt.edgeDistance[3]));
+#endif
+ float v = 0.8;
+ vec4 Cedge = vec4(Cfill.r*v, Cfill.g*v, Cfill.b*v, 1);
+ float p = exp2(-2 * d * d);
+
+#if defined(GEOMETRY_OUT_WIRE)
+ if (p < 0.25) discard;
+#endif
+
+ Cfill.rgb = mix(Cfill.rgb, Cedge.rgb, p);
+#endif
+ return Cfill;
+}
+
+vec4
+getAdaptivePatchColor(ivec3 patchParam)
+{
+ const vec4 patchColors[7*6] = vec4[7*6](
+ vec4(1.0f, 1.0f, 1.0f, 1.0f), // regular
+ vec4(0.0f, 1.0f, 1.0f, 1.0f), // regular pattern 0
+ vec4(0.0f, 0.5f, 1.0f, 1.0f), // regular pattern 1
+ vec4(0.0f, 0.5f, 0.5f, 1.0f), // regular pattern 2
+ vec4(0.5f, 0.0f, 1.0f, 1.0f), // regular pattern 3
+ vec4(1.0f, 0.5f, 1.0f, 1.0f), // regular pattern 4
+
+ vec4(1.0f, 0.5f, 0.5f, 1.0f), // single crease
+ vec4(1.0f, 0.70f, 0.6f, 1.0f), // single crease pattern 0
+ vec4(1.0f, 0.65f, 0.6f, 1.0f), // single crease pattern 1
+ vec4(1.0f, 0.60f, 0.6f, 1.0f), // single crease pattern 2
+ vec4(1.0f, 0.55f, 0.6f, 1.0f), // single crease pattern 3
+ vec4(1.0f, 0.50f, 0.6f, 1.0f), // single crease pattern 4
+
+ vec4(0.8f, 0.0f, 0.0f, 1.0f), // boundary
+ vec4(0.0f, 0.0f, 0.75f, 1.0f), // boundary pattern 0
+ vec4(0.0f, 0.2f, 0.75f, 1.0f), // boundary pattern 1
+ vec4(0.0f, 0.4f, 0.75f, 1.0f), // boundary pattern 2
+ vec4(0.0f, 0.6f, 0.75f, 1.0f), // boundary pattern 3
+ vec4(0.0f, 0.8f, 0.75f, 1.0f), // boundary pattern 4
+
+ vec4(0.0f, 1.0f, 0.0f, 1.0f), // corner
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 0
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 1
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 2
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 3
+ vec4(0.5f, 1.0f, 0.5f, 1.0f), // corner pattern 4
+
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+ vec4(1.0f, 1.0f, 0.0f, 1.0f), // gregory
+
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+ vec4(1.0f, 0.5f, 0.0f, 1.0f), // gregory boundary
+
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f), // gregory basis
+ vec4(1.0f, 0.7f, 0.3f, 1.0f) // gregory basis
+ );
+
+ int patchType = 0;
+
+ int edgeCount = bitCount(OsdGetPatchBoundaryMask(patchParam));
+ if (edgeCount == 1) {
+ patchType = 2; // BOUNDARY
+ }
+ if (edgeCount > 1) {
+ patchType = 3; // CORNER (not correct for patches that are not isolated)
+ }
+
+#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
+ // check this after boundary/corner since single crease patch also has edgeCount.
+ if (inpt.vSegments.y > 0) {
+ patchType = 1;
+ }
+#elif defined OSD_PATCH_GREGORY
+ patchType = 4;
+#elif defined OSD_PATCH_GREGORY_BOUNDARY
+ patchType = 5;
+#elif defined OSD_PATCH_GREGORY_BASIS
+ patchType = 6;
+#elif defined OSD_PATCH_GREGORY_TRIANGLE
+ patchType = 6;
+#endif
+
+ int pattern = bitCount(OsdGetPatchTransitionMask(patchParam));
+
+ return patchColors[6*patchType + pattern];
+}
+
+vec4
+getAdaptiveDepthColor(ivec3 patchParam)
+{
+ // Represent depth with repeating cycle of four colors:
+ const vec4 depthColors[4] = vec4[4](
+ vec4(0.0f, 0.5f, 0.5f, 1.0f),
+ vec4(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.0f, 1.0f, 1.0f, 1.0f),
+ vec4(0.5f, 1.0f, 0.5f, 1.0f)
+ );
+ return depthColors[OsdGetPatchRefinementLevel(patchParam) & 3];
+}
+
+#if defined(PRIM_QUAD) || defined(PRIM_TRI)
+void
+main()
+{
+ vec3 N = (gl_FrontFacing ? inpt.v.normal : -inpt.v.normal);
+
+#if defined(SHADING_VARYING_COLOR)
+ vec4 color = vec4(inpt.color, 1);
+#elif defined(SHADING_FACEVARYING_COLOR)
+ // generating a checkerboard pattern
+ vec4 color = vec4(inpt.color.rg,
+ int(floor(20*inpt.color.r)+floor(20*inpt.color.g))&1, 1);
+#elif defined(SHADING_PATCH_TYPE)
+ vec4 color = getAdaptivePatchColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_DEPTH)
+ vec4 color = getAdaptiveDepthColor(OsdGetPatchParam(OsdGetPatchIndex(gl_PrimitiveID)));
+#elif defined(SHADING_PATCH_COORD)
+ vec4 color = vec4(inpt.v.patchCoord.xy, 0, 1);
+#elif defined(SHADING_MATERIAL)
+ vec4 color = diffuseColor;
+#else
+ vec4 color = vec4(1, 1, 1, 1);
+#endif
+
+ vec4 Cf = lighting(color, inpt.v.position.xyz, N);
+
+#if defined(SHADING_NORMAL)
+ Cf.rgb = N;
+#endif
+
+#if defined(GEOMETRY_OUT_WIRE) || defined(GEOMETRY_OUT_LINE)
+ Cf = edgeColor(Cf, inpt.edgeDistance);
+#endif
+
+ outColor = Cf;
+}
+#endif
+
+#endif
+
+