Lagrange
mesh_subdivision.h
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/Mesh.h>
15#include <lagrange/MeshTrait.h>
16#include <lagrange/create_mesh.h>
17#include <lagrange/utils/range.h>
18
19// clang-format off
20#include <lagrange/utils/warnoff.h>
21#include <opensubdiv/far/primvarRefiner.h>
22#include <opensubdiv/far/topologyDescriptor.h>
23#include <lagrange/utils/warnon.h>
24// clang-format on
25
26#include <Eigen/Core>
27#include <memory>
28#include <vector>
29
30
31namespace lagrange {
32namespace subdivision {
33
34using SubdivisionScheme = OpenSubdiv::Sdc::SchemeType;
35
36namespace internal {
37
38// Vertex container implementation for OSD
39template <typename MeshType>
41{
42 using VertexType = typename MeshType::VertexType;
43 OSDVertex() = default;
44 OSDVertex(OSDVertex const& src) { m_position = src.m_position; }
45
46 // Required interface
47 void Clear(void* = 0) { m_position.setZero(); }
48 void AddWithWeight(OSDVertex const& src, float weight)
49 {
50 m_position += (weight * src.m_position);
51 }
52
53 // Optional interface (used by Lagrange)
54 void SetPosition(const VertexType& pos) { m_position = pos; }
55 const VertexType& GetPosition() const { return m_position; }
56
57private:
58 VertexType m_position;
59};
60
61// Face-varying container implementation.
62template <typename MeshType>
63struct OSDUV
64{
65 using UVType = typename MeshType::UVType;
66 OSDUV() {}
67 OSDUV(OSDUV const& src) { m_position = src.m_position; }
68
69 // Minimal required interface ----------------------
70 void Clear(void* = 0) { m_position.setZero(); }
71 void AddWithWeight(OSDUV const& src, float weight) { m_position += (weight * src.m_position); }
72
73 // Optional interface (used by Lagrange)
74 void SetPosition(const UVType& pos) { m_position = pos; }
75 const UVType& GetPosition() const { return m_position; }
76
77private:
78 // Basic 'uv' layout channel
79 UVType m_position;
80};
81
82} // namespace internal
83
84
85// Main Entry Point
86
87template <typename input_meshType, typename output_meshType>
88std::unique_ptr<output_meshType> subdivide_mesh(
89 const input_meshType& input_mesh, // input lagrange mesh
90 SubdivisionScheme scheme_type, // loop? catmull-clark?
91 int maxlevel, // uniform subdivision level requested
92 OpenSubdiv::Sdc::Options::VtxBoundaryInterpolation vertexInterp =
93 OpenSubdiv::Sdc::Options::VTX_BOUNDARY_EDGE_ONLY,
94 OpenSubdiv::Sdc::Options::FVarLinearInterpolation primvarInterp =
95 OpenSubdiv::Sdc::Options::FVAR_LINEAR_ALL)
96{
97 static_assert(MeshTrait<input_meshType>::is_mesh(), "Input type is not Mesh");
98 static_assert(
99 std::is_same<typename input_meshType::Index, typename output_meshType::Index>::value,
100 "Meshes must have same Index type");
101
102 using namespace OpenSubdiv;
103 using Descriptor = Far::TopologyDescriptor;
104 using namespace internal;
105 using OSDIndex = Vtr::Index;
106
107 if (scheme_type == SubdivisionScheme::SCHEME_LOOP) {
109 input_mesh.get_vertex_per_facet() == 3,
110 "Loop Subdivision only supports triangle meshes");
111 }
112
113 // Subdivision Options
114 Sdc::Options options;
115 options.SetVtxBoundaryInterpolation(vertexInterp);
116 options.SetFVarLinearInterpolation(primvarInterp);
117
118 // Input info
119 const auto num_vertices = input_mesh.get_num_vertices();
120 const auto num_facets = input_mesh.get_num_facets();
121 const auto input_vertex_per_facet = input_mesh.get_vertex_per_facet();
122 const auto output_vertex_per_facet = output_meshType::FacetArray::ColsAtCompileTime==Eigen::Dynamic?
123 input_vertex_per_facet:output_meshType::FacetArray::ColsAtCompileTime;
124
125 if (input_vertex_per_facet == 3 && output_vertex_per_facet == 4) {
127 maxlevel > 0,
128 "Only non-zero-level subdivision is supported when the input is triangle and the "
129 "output is quadrangle.");
130 }
131
132 // OpenSubdiv mesh
133 Descriptor desc;
134 desc.numVertices = num_vertices;
135 desc.numFaces = num_facets;
136 // descriptor wants an index array and an array of number of elements
137 std::vector<int> verts_per_face(num_facets, input_vertex_per_facet);
138 desc.numVertsPerFace = verts_per_face.data();
139 la_runtime_assert(desc.numVertsPerFace != nullptr, "Invalid Vertices");
140 const auto& facets = input_mesh.get_facets();
141 std::vector<OSDIndex> vert_indices;
142 vert_indices.resize(num_facets * input_vertex_per_facet);
143 for (auto i : range(desc.numFaces)) {
144 for (auto j : range(input_vertex_per_facet)) {
145 vert_indices[i * input_vertex_per_facet + j] = safe_cast<OSDIndex>(facets(i, j));
146 }
147 }
148 desc.vertIndicesPerFace = vert_indices.data();
149 la_runtime_assert(desc.vertIndicesPerFace != nullptr, "Invalid Facets");
150
151 // Create a face-varying channel descriptor
152 const bool hasUvs = input_mesh.is_uv_initialized();
153 std::vector<OSDIndex> uv_indices;
154 const int channelUV = 0;
155 const int numChannels = 1;
156 Descriptor::FVarChannel channels[numChannels];
157 if (hasUvs) {
158 channels[channelUV].numValues = (int)input_mesh.get_uv().rows();
159 const auto& inputUVIndices = input_mesh.get_uv_indices();
160 uv_indices.resize(inputUVIndices.size());
161 for (auto i : range(desc.numFaces)) {
162 for (auto j : range(input_vertex_per_facet)) {
163 uv_indices[i * input_vertex_per_facet + j] =
164 safe_cast<OSDIndex>(inputUVIndices(i, j));
165 }
166 }
167 channels[channelUV].valueIndices = uv_indices.data();
168
169 // Add the channel topology to the main descriptor
170 desc.numFVarChannels = numChannels;
171 desc.fvarChannels = channels;
172 }
173
174 // Instantiate a Far::TopologyRefiner from the descriptor
175 std::unique_ptr<Far::TopologyRefiner> refiner(Far::TopologyRefinerFactory<Descriptor>::Create(
176 desc, Far::TopologyRefinerFactory<Descriptor>::Options(scheme_type, options)));
177
178 // Uniformly refine the topology up to 'maxlevel'
179 {
180 // note: fullTopologyInLastLevel must be true to work with face-varying data
181 Far::TopologyRefiner::UniformOptions refineOptions(maxlevel);
182 refineOptions.fullTopologyInLastLevel = true;
183 refiner->RefineUniform(refineOptions);
184 }
185
186 // Allocate a buffer for vertex primvar data. The buffer length is set to
187 // be the sum of all children vertices up to the highest level of refinement.
188 std::vector<OSDVertex<output_meshType>> vbuffer(refiner->GetNumVerticesTotal());
189 OSDVertex<output_meshType>* outputVerts = &vbuffer[0];
190 // Initialize coarse mesh positions
191 int nInputVerts = input_mesh.get_num_vertices();
192 for (int i = 0; i < nInputVerts; ++i)
193 outputVerts[i].SetPosition(
194 input_mesh.get_vertices().row(i).template cast<typename output_meshType::Scalar>());
195
196 OSDUV<output_meshType>* outputUvs = nullptr;
197 std::vector<OSDUV<output_meshType>> fvBufferUV;
198 if (hasUvs) {
199 // Allocate the first channel of 'face-varying' primvars (UVs)
200 fvBufferUV.resize(refiner->GetNumFVarValuesTotal(channelUV));
201 outputUvs = &fvBufferUV[0];
202 // initialize
203 for (int i = 0; i < input_mesh.get_uv().rows(); ++i) {
204 outputUvs[i].SetPosition(
205 input_mesh.get_uv().row(i).template cast<typename output_meshType::Scalar>());
206 }
207 }
208
209 // Interpolate both vertex and face-varying primvar data
210 Far::PrimvarRefiner primvarRefiner(*refiner);
211 OSDVertex<output_meshType>* srcVert = outputVerts;
212 OSDUV<output_meshType>* srcFVarUV = outputUvs;
213 // FVarVertexColor * srcFVarColor = fvVertsColor;
214 for (int level = 1; level <= maxlevel; ++level) {
215 OSDVertex<output_meshType>* dstVert =
216 srcVert + refiner->GetLevel(level - 1).GetNumVertices();
217 OSDUV<output_meshType>* dstFVarUV = outputUvs;
218 if (hasUvs)
219 dstFVarUV = srcFVarUV + refiner->GetLevel(level - 1).GetNumFVarValues(channelUV);
220
221 primvarRefiner.Interpolate(level, srcVert, dstVert);
222
223 if (hasUvs) primvarRefiner.InterpolateFaceVarying(level, srcFVarUV, dstFVarUV, channelUV);
224 srcVert = dstVert;
225 if (hasUvs) srcFVarUV = dstFVarUV;
226 }
227
228 // TODO: approximate normals
229
230 // Generate output
231 Far::TopologyLevel const& refLastLevel = refiner->GetLevel(maxlevel);
232 int nOutputVerts = refLastLevel.GetNumVertices();
233 int nOutputFaces = refLastLevel.GetNumFaces();
234 bool triangulate = false;
235 if (scheme_type == SubdivisionScheme::SCHEME_LOOP) {
236 // outputs are all triangles. Not trying to quad-fy here.
237 assert(output_vertex_per_facet == 3);
238 } else {
239 // only accepting output triangles or quads
240 assert(output_vertex_per_facet == 3 || output_vertex_per_facet == 4);
241 if (output_vertex_per_facet == 3) {
242 // never change the topology for zero-level + triangle mesh
243 if (maxlevel > 0 || input_vertex_per_facet > 3) {
244 triangulate = true;
245 nOutputFaces *= 2;
246 }
247 }
248 }
249
250 // copy verts
251 int firstOfLastVerts = refiner->GetNumVerticesTotal() - nOutputVerts;
252 typename output_meshType::VertexArray V;
253 V.resize(nOutputVerts, 3);
254 for (int iVert = 0; iVert < nOutputVerts; ++iVert) {
255 typename output_meshType::VertexType pos =
256 outputVerts[firstOfLastVerts + iVert].GetPosition();
257 V.row(iVert) = pos;
258 }
259
260 // copy faces
261 typename output_meshType::FacetArray F;
262 F.resize(nOutputFaces, F.cols());
263 for (int iFace = 0; iFace < refLastLevel.GetNumFaces(); ++iFace) {
264 Far::ConstIndexArray facetIndices = refLastLevel.GetFaceVertices(iFace);
265 if (triangulate) {
266 F(iFace * 2, 0) = facetIndices[0];
267 F(iFace * 2, 1) = facetIndices[1];
268 F(iFace * 2, 2) = facetIndices[2];
269 F(iFace * 2 + 1, 0) = facetIndices[0];
270 F(iFace * 2 + 1, 1) = facetIndices[2];
271 F(iFace * 2 + 1, 2) = facetIndices[3];
272 } else {
273 for (int i = 0; i < facetIndices.size(); ++i) {
274 F(iFace, i) = facetIndices[i];
275 }
276 }
277 }
278
279 std::unique_ptr<output_meshType> output_mesh = create_mesh(std::move(V), std::move(F));
280
281 // copy uvs and indices
282 if (hasUvs) {
283 int nOutputUvs = refLastLevel.GetNumFVarValues(channelUV);
284 int firstOfLastUvs = refiner->GetNumFVarValuesTotal(channelUV) - nOutputUvs;
285 typename output_meshType::UVArray UV;
286 typename output_meshType::FacetArray UVF;
287 UV.resize(nOutputUvs, 2);
288 UVF.resize(nOutputFaces, output_vertex_per_facet);
289 for (int iFVVert = 0; iFVVert < nOutputUvs; ++iFVVert) {
290 typename output_meshType::UVType uv = outputUvs[firstOfLastUvs + iFVVert].GetPosition();
291 UV.row(iFVVert) = uv;
292 }
293 for (int iFace = 0; iFace < refLastLevel.GetNumFaces(); ++iFace) {
294 Far::ConstIndexArray uvIndices = refLastLevel.GetFaceFVarValues(iFace, channelUV);
295 if (triangulate) {
296 UVF(iFace * 2, 0) = uvIndices[0];
297 UVF(iFace * 2, 1) = uvIndices[1];
298 UVF(iFace * 2, 2) = uvIndices[2];
299 UVF(iFace * 2 + 1, 0) = uvIndices[0];
300 UVF(iFace * 2 + 1, 1) = uvIndices[2];
301 UVF(iFace * 2 + 1, 2) = uvIndices[3];
302 } else {
303 for (int i = 0; i < uvIndices.size(); ++i) {
304 UVF(iFace, i) = uvIndices[i];
305 }
306 }
307 }
308 output_mesh->initialize_uv(UV, UVF);
309 }
310
311 // TODO: add colors too...
312 return output_mesh;
313}
314} // namespace subdivision
315} // namespace lagrange
@ UV
Mesh attribute must have exactly 2 channels.
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
SchemeType
Subdivision scheme.
Definition: mesh_subdivision.h:29
Main namespace for Lagrange.
Definition: AABBIGL.h:30
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition: create_mesh.h:39
MeshTrait class provide compiler check for different mesh types.
Definition: MeshTrait.h:108
Definition: mesh_subdivision.h:64
Definition: mesh_subdivision.h:41