Lagrange
Loading...
Searching...
No Matches
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>
40struct OSDVertex
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 =
123 output_meshType::FacetArray::ColsAtCompileTime == Eigen::Dynamic
124 ? input_vertex_per_facet
125 : output_meshType::FacetArray::ColsAtCompileTime;
126
127 if (input_vertex_per_facet == 3 && output_vertex_per_facet == 4) {
129 maxlevel > 0,
130 "Only non-zero-level subdivision is supported when the input is triangle and the "
131 "output is quadrangle.");
132 }
133
134 // OpenSubdiv mesh
135 Descriptor desc;
136 desc.numVertices = num_vertices;
137 desc.numFaces = num_facets;
138 // descriptor wants an index array and an array of number of elements
139 std::vector<int> verts_per_face(num_facets, input_vertex_per_facet);
140 desc.numVertsPerFace = verts_per_face.data();
141 la_runtime_assert(desc.numVertsPerFace != nullptr, "Invalid Vertices");
142 const auto& facets = input_mesh.get_facets();
143 std::vector<OSDIndex> vert_indices;
144 vert_indices.resize(num_facets * input_vertex_per_facet);
145 for (auto i : range(desc.numFaces)) {
146 for (auto j : range(input_vertex_per_facet)) {
147 vert_indices[i * input_vertex_per_facet + j] = safe_cast<OSDIndex>(facets(i, j));
148 }
149 }
150 desc.vertIndicesPerFace = vert_indices.data();
151 la_runtime_assert(desc.vertIndicesPerFace != nullptr, "Invalid Facets");
152
153 // Create a face-varying channel descriptor
154 const bool hasUvs = input_mesh.is_uv_initialized();
155 std::vector<OSDIndex> uv_indices;
156 const int channelUV = 0;
157 const int numChannels = 1;
158 Descriptor::FVarChannel channels[numChannels];
159 if (hasUvs) {
160 channels[channelUV].numValues = (int)input_mesh.get_uv().rows();
161 const auto& inputUVIndices = input_mesh.get_uv_indices();
162 uv_indices.resize(inputUVIndices.size());
163 for (auto i : range(desc.numFaces)) {
164 for (auto j : range(input_vertex_per_facet)) {
165 uv_indices[i * input_vertex_per_facet + j] =
166 safe_cast<OSDIndex>(inputUVIndices(i, j));
167 }
168 }
169 channels[channelUV].valueIndices = uv_indices.data();
170
171 // Add the channel topology to the main descriptor
172 desc.numFVarChannels = numChannels;
173 desc.fvarChannels = channels;
174 }
175
176 // Instantiate a Far::TopologyRefiner from the descriptor
177 std::unique_ptr<Far::TopologyRefiner> refiner(
178 Far::TopologyRefinerFactory<Descriptor>::Create(
179 desc,
180 Far::TopologyRefinerFactory<Descriptor>::Options(scheme_type, options)));
181
182 // Uniformly refine the topology up to 'maxlevel'
183 {
184 // note: fullTopologyInLastLevel must be true to work with face-varying data
185 Far::TopologyRefiner::UniformOptions refineOptions(maxlevel);
186 refineOptions.fullTopologyInLastLevel = true;
187 refiner->RefineUniform(refineOptions);
188 }
189
190 // Allocate a buffer for vertex primvar data. The buffer length is set to
191 // be the sum of all children vertices up to the highest level of refinement.
192 std::vector<OSDVertex<output_meshType>> vbuffer(refiner->GetNumVerticesTotal());
193 OSDVertex<output_meshType>* outputVerts = &vbuffer[0];
194 // Initialize coarse mesh positions
195 int nInputVerts = input_mesh.get_num_vertices();
196 for (int i = 0; i < nInputVerts; ++i)
197 outputVerts[i].SetPosition(
198 input_mesh.get_vertices().row(i).template cast<typename output_meshType::Scalar>());
199
200 OSDUV<output_meshType>* outputUvs = nullptr;
201 std::vector<OSDUV<output_meshType>> fvBufferUV;
202 if (hasUvs) {
203 // Allocate the first channel of 'face-varying' primvars (UVs)
204 fvBufferUV.resize(refiner->GetNumFVarValuesTotal(channelUV));
205 outputUvs = &fvBufferUV[0];
206 // initialize
207 for (int i = 0; i < input_mesh.get_uv().rows(); ++i) {
208 outputUvs[i].SetPosition(
209 input_mesh.get_uv().row(i).template cast<typename output_meshType::Scalar>());
210 }
211 }
212
213 // Interpolate both vertex and face-varying primvar data
214 Far::PrimvarRefiner primvarRefiner(*refiner);
215 OSDVertex<output_meshType>* srcVert = outputVerts;
216 OSDUV<output_meshType>* srcFVarUV = outputUvs;
217 // FVarVertexColor * srcFVarColor = fvVertsColor;
218 for (int level = 1; level <= maxlevel; ++level) {
219 OSDVertex<output_meshType>* dstVert =
220 srcVert + refiner->GetLevel(level - 1).GetNumVertices();
221 OSDUV<output_meshType>* dstFVarUV = outputUvs;
222 if (hasUvs)
223 dstFVarUV = srcFVarUV + refiner->GetLevel(level - 1).GetNumFVarValues(channelUV);
224
225 primvarRefiner.Interpolate(level, srcVert, dstVert);
226
227 if (hasUvs) primvarRefiner.InterpolateFaceVarying(level, srcFVarUV, dstFVarUV, channelUV);
228 srcVert = dstVert;
229 if (hasUvs) srcFVarUV = dstFVarUV;
230 }
231
232 // TODO: approximate normals
233
234 // Generate output
235 Far::TopologyLevel const& refLastLevel = refiner->GetLevel(maxlevel);
236 int nOutputVerts = refLastLevel.GetNumVertices();
237 int nOutputFaces = refLastLevel.GetNumFaces();
238 bool triangulate = false;
239 if (scheme_type == SubdivisionScheme::SCHEME_LOOP) {
240 // outputs are all triangles. Not trying to quad-fy here.
241 assert(output_vertex_per_facet == 3);
242 } else {
243 // only accepting output triangles or quads
244 assert(output_vertex_per_facet == 3 || output_vertex_per_facet == 4);
245 if (output_vertex_per_facet == 3) {
246 // never change the topology for zero-level + triangle mesh
247 if (maxlevel > 0 || input_vertex_per_facet > 3) {
248 triangulate = true;
249 nOutputFaces *= 2;
250 }
251 }
252 }
253
254 // copy verts
255 int firstOfLastVerts = refiner->GetNumVerticesTotal() - nOutputVerts;
256 typename output_meshType::VertexArray V;
257 V.resize(nOutputVerts, 3);
258 for (int iVert = 0; iVert < nOutputVerts; ++iVert) {
259 typename output_meshType::VertexType pos =
260 outputVerts[firstOfLastVerts + iVert].GetPosition();
261 V.row(iVert) = pos;
262 }
263
264 // copy faces
265 typename output_meshType::FacetArray F;
266 F.resize(nOutputFaces, F.cols());
267 for (int iFace = 0; iFace < refLastLevel.GetNumFaces(); ++iFace) {
268 Far::ConstIndexArray facetIndices = refLastLevel.GetFaceVertices(iFace);
269 if (triangulate) {
270 F(iFace * 2, 0) = facetIndices[0];
271 F(iFace * 2, 1) = facetIndices[1];
272 F(iFace * 2, 2) = facetIndices[2];
273 F(iFace * 2 + 1, 0) = facetIndices[0];
274 F(iFace * 2 + 1, 1) = facetIndices[2];
275 F(iFace * 2 + 1, 2) = facetIndices[3];
276 } else {
277 for (int i = 0; i < facetIndices.size(); ++i) {
278 F(iFace, i) = facetIndices[i];
279 }
280 }
281 }
282
283 std::unique_ptr<output_meshType> output_mesh = create_mesh(std::move(V), std::move(F));
284
285 // copy uvs and indices
286 if (hasUvs) {
287 int nOutputUvs = refLastLevel.GetNumFVarValues(channelUV);
288 int firstOfLastUvs = refiner->GetNumFVarValuesTotal(channelUV) - nOutputUvs;
289 typename output_meshType::UVArray UV;
290 typename output_meshType::FacetArray UVF;
291 UV.resize(nOutputUvs, 2);
292 UVF.resize(nOutputFaces, output_vertex_per_facet);
293 for (int iFVVert = 0; iFVVert < nOutputUvs; ++iFVVert) {
294 typename output_meshType::UVType uv = outputUvs[firstOfLastUvs + iFVVert].GetPosition();
295 UV.row(iFVVert) = uv;
296 }
297 for (int iFace = 0; iFace < refLastLevel.GetNumFaces(); ++iFace) {
298 Far::ConstIndexArray uvIndices = refLastLevel.GetFaceFVarValues(iFace, channelUV);
299 if (triangulate) {
300 UVF(iFace * 2, 0) = uvIndices[0];
301 UVF(iFace * 2, 1) = uvIndices[1];
302 UVF(iFace * 2, 2) = uvIndices[2];
303 UVF(iFace * 2 + 1, 0) = uvIndices[0];
304 UVF(iFace * 2 + 1, 1) = uvIndices[2];
305 UVF(iFace * 2 + 1, 2) = uvIndices[3];
306 } else {
307 for (int i = 0; i < uvIndices.size(); ++i) {
308 UVF(iFace, i) = uvIndices[i];
309 }
310 }
311 }
312 output_mesh->initialize_uv(UV, UVF);
313 }
314
315 // TODO: add colors too...
316 return output_mesh;
317}
318} // namespace subdivision
319} // namespace lagrange
@ UV
Mesh attribute must have exactly 2 channels.
Definition AttributeFwd.h:62
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50
Subdivision surfaces.
Definition mesh_subdivision.h:32
Main namespace for Lagrange.
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