Blender V5.0
osd.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#ifdef WITH_OPENSUBDIV
6
7# include "subd/osd.h"
8
9# include "scene/attribute.h"
10# include "scene/mesh.h"
11
12# include "util/log.h"
13
14/* Specialization of TopologyRefinerFactory for OsdMesh */
15
17
18using namespace ccl;
19
20template<>
21bool TopologyRefinerFactory<OsdMesh>::resizeComponentTopology(TopologyRefiner &refiner,
22 OsdMesh const &osd_mesh)
23{
24 const Mesh &mesh = osd_mesh.mesh;
25
26 const int num_base_verts = mesh.get_num_subd_base_verts();
27 const int num_base_faces = mesh.get_num_subd_faces();
28 const int *subd_num_corners = mesh.get_subd_num_corners().data();
29
30 setNumBaseVertices(refiner, num_base_verts);
31 setNumBaseFaces(refiner, num_base_faces);
32
33 for (int i = 0; i < num_base_faces; i++) {
34 setNumBaseFaceVertices(refiner, i, subd_num_corners[i]);
35 }
36
37 return true;
38}
39
40template<>
41bool TopologyRefinerFactory<OsdMesh>::assignComponentTopology(TopologyRefiner &refiner,
42 OsdMesh const &osd_mesh)
43{
44 const Mesh &mesh = osd_mesh.mesh;
45
46 const int num_base_faces = mesh.get_num_subd_faces();
47
48 const int *subd_face_corners = mesh.get_subd_face_corners().data();
49 const int *subd_start_corner = mesh.get_subd_start_corner().data();
50 const int *subd_num_corners = mesh.get_subd_num_corners().data();
51
52 for (int i = 0; i < num_base_faces; i++) {
53 IndexArray face_verts = getBaseFaceVertices(refiner, i);
54
55 const int start_corner = subd_start_corner[i];
56 const int *corner = &subd_face_corners[start_corner];
57
58 for (int j = 0; j < subd_num_corners[i]; j++, corner++) {
59 face_verts[j] = *corner;
60 }
61 }
62
63 return true;
64}
65
66template<>
67bool TopologyRefinerFactory<OsdMesh>::assignComponentTags(TopologyRefiner &refiner,
68 OsdMesh const &osd_mesh)
69{
70 const Mesh &mesh = osd_mesh.mesh;
71
72 /* Historical maximum crease weight used at Pixar, influencing the maximum in OpenSubDiv. */
73 static constexpr float CREASE_SCALE = 10.0f;
74
75 const size_t num_creases = mesh.get_subd_creases_weight().size();
76 const size_t num_vertex_creases = mesh.get_subd_vert_creases().size();
77
78 /* The last loop is over the vertices, so early exit to avoid iterating them needlessly. */
79 if (num_creases == 0 && num_vertex_creases == 0) {
80 return true;
81 }
82
83 for (int i = 0; i < num_creases; i++) {
84 const Mesh::SubdEdgeCrease crease = mesh.get_subd_crease(i);
85 const Index edge = findBaseEdge(refiner, crease.v[0], crease.v[1]);
86
87 if (edge != INDEX_INVALID) {
88 setBaseEdgeSharpness(refiner, edge, crease.crease * CREASE_SCALE);
89 }
90 }
91
92 std::map<int, float> vertex_creases;
93
94 for (size_t i = 0; i < num_vertex_creases; ++i) {
95 const int vertex_idx = mesh.get_subd_vert_creases()[i];
96 const float weight = mesh.get_subd_vert_creases_weight()[i];
97
98 vertex_creases[vertex_idx] = weight * CREASE_SCALE;
99 }
100
101 const int num_base_verts = mesh.get_num_subd_base_verts();
102
103 for (int i = 0; i < num_base_verts; i++) {
104 float sharpness = 0.0f;
105 const std::map<int, float>::const_iterator iter = vertex_creases.find(i);
106
107 if (iter != vertex_creases.end()) {
108 sharpness = iter->second;
109 }
110
111 const ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
112
113 if (vert_edges.size() == 2) {
114 const float sharpness0 = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
115 const float sharpness1 = refiner.getLevel(0).getEdgeSharpness(vert_edges[1]);
116
117 sharpness += min(sharpness0, sharpness1);
118 sharpness = min(sharpness, CREASE_SCALE);
119 }
120
121 if (sharpness != 0.0f) {
122 setBaseVertexSharpness(refiner, i, sharpness);
123 }
124 }
125
126 return true;
127}
128
129template<typename T>
130static void merge_smooth_fvar(const Mesh &mesh,
131 const Attribute &subd_attr,
132 OsdMesh::MergedFVar &merged_fvar,
133 vector<int> &merged_next,
134 vector<int> &merged_face_corners)
135{
136 const int num_base_verts = mesh.get_num_subd_base_verts();
137 const int num_base_faces = mesh.get_num_subd_faces();
138 const int *subd_face_corners = mesh.get_subd_face_corners().data();
139
140 const T *values = reinterpret_cast<const T *>(subd_attr.data());
141
142 merged_fvar.values.resize(num_base_verts * sizeof(T));
143
144 // Merge identical corner values with the same vertex. The first value is stored at the vertex
145 // index, and any different values are pushed backed onto the array. merged_next creates a
146 // linked list between all values for the same vertex.
147 const int state_uninitialized = 0;
148 const int state_end = -1;
149 merged_next.resize(num_base_verts, state_uninitialized);
150
151 for (int f = 0, i = 0; f < num_base_faces; f++) {
152 Mesh::SubdFace face = mesh.get_subd_face(f);
153
154 for (int corner = 0; corner < face.num_corners; corner++) {
155 int v = subd_face_corners[face.start_corner + corner];
156 const T value = values[i++];
157
158 if (merged_next[v] == state_uninitialized) {
159 // First corner to initialize vertex.
160 reinterpret_cast<T *>(merged_fvar.values.data())[v] = value;
161 merged_next[v] = state_end;
162 merged_face_corners.push_back(v);
163 }
164 else {
165 // Find vertex with matching value, following linked list per vertex.
166 int v_prev = v;
167 for (; v != state_end; v_prev = v, v = merged_next[v]) {
168 if (reinterpret_cast<T *>(merged_fvar.values.data())[v] == value) {
169 // Matching value found, reuse merged vertex.
170 merged_face_corners.push_back(v);
171 break;
172 }
173 }
174
175 if (v == state_end) {
176 // Non-matching value, add new merged vertex and add to linked list.
177 const int next = merged_next.size();
178 merged_fvar.values.resize((next + 1) * sizeof(T));
179 reinterpret_cast<T *>(merged_fvar.values.data())[next] = value;
180 merged_next.push_back(state_end);
181 merged_next[v_prev] = next;
182 merged_face_corners.push_back(next);
183 }
184 }
185 }
186 }
187}
188
189template<>
190bool TopologyRefinerFactory<OsdMesh>::assignFaceVaryingTopology(TopologyRefiner &refiner,
191 OsdMesh const &osd_mesh)
192{
193 const Mesh &mesh = osd_mesh.mesh;
194 auto &merged_fvars = const_cast<OsdMesh &>(osd_mesh).merged_fvars;
195
196 for (const Attribute &subd_attr : mesh.subd_attributes.attributes) {
197 if (!osd_mesh.use_smooth_fvar(subd_attr)) {
198 continue;
199 }
200
201 // Created merged FVar, for use in subdivide_attribute_corner_smooth.
202 OsdMesh::MergedFVar merged_fvar{subd_attr};
203
204 vector<int> merged_next;
205 vector<int> merged_face_corners;
206
207 if (subd_attr.element == ATTR_ELEMENT_CORNER_BYTE) {
208 merge_smooth_fvar<uchar4>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
209 }
210 else if (Attribute::same_storage(subd_attr.type, TypeFloat)) {
211 merge_smooth_fvar<float>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
212 }
213 else if (Attribute::same_storage(subd_attr.type, TypeFloat2)) {
214 merge_smooth_fvar<float2>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
215 }
216 else if (Attribute::same_storage(subd_attr.type, TypeVector)) {
217 merge_smooth_fvar<float3>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
218 }
219 else if (Attribute::same_storage(subd_attr.type, TypeFloat4)) {
220 merge_smooth_fvar<float4>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
221 }
222
223 // Create FVar channel and topology for OpenUSD.
224 merged_fvar.channel = createBaseFVarChannel(refiner, merged_next.size());
225
226 const int num_base_faces = mesh.get_num_subd_faces();
227 for (int f = 0, i = 0; f < num_base_faces; f++) {
228 Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, f, merged_fvar.channel);
229 const int num_corners = dst_face_uvs.size();
230 for (int corner = 0; corner < num_corners; corner++) {
231 dst_face_uvs[corner] = merged_face_corners[i++];
232 }
233 }
234
235 merged_fvars.push_back(std::move(merged_fvar));
236 }
237
238 return true;
239}
240
241template<>
242void TopologyRefinerFactory<OsdMesh>::reportInvalidTopology(TopologyError /*err_code*/,
243 char const *msg,
244 OsdMesh const &osd_mesh)
245{
246 const Mesh &mesh = osd_mesh.mesh;
247 LOG_WARNING << "Invalid subdivision topology for '" << mesh.name.c_str() << "': " << msg;
248}
249} // namespace OpenSubdiv::OPENSUBDIV_VERSION::Far
250
252
253/* OsdMesh */
254
255Sdc::Options OsdMesh::sdc_options()
256{
257 Sdc::Options options;
258 switch (mesh.get_subdivision_fvar_interpolation()) {
260 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_NONE);
261 break;
263 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_ONLY);
264 break;
266 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_PLUS1);
267 break;
269 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_PLUS2);
270 break;
272 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_BOUNDARIES);
273 break;
275 options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_ALL);
276 break;
277 }
278 switch (mesh.get_subdivision_boundary_interpolation()) {
280 options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_NONE);
281 break;
283 options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
284 break;
286 options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
287 break;
288 }
289 return options;
290}
291
292bool OsdMesh::use_smooth_fvar(const Attribute &attr) const
293{
294 return mesh.get_subdivision_fvar_interpolation() != Mesh::SUBDIVISION_FVAR_LINEAR_ALL &&
296 (attr.std == ATTR_STD_UV || (attr.flags & ATTR_SUBDIVIDE_SMOOTH_FVAR));
297}
298
299bool OsdMesh::use_smooth_fvar() const
300{
301 for (const Attribute &attr : mesh.subd_attributes.attributes) {
302 if (use_smooth_fvar(attr)) {
303 return true;
304 }
305 }
306
307 return false;
308}
309
310/* OsdData */
311
312void OsdData::build(OsdMesh &osd_mesh)
313{
314 /* create refiner */
315 refiner.reset(Far::TopologyRefinerFactory<OsdMesh>::Create(
316 osd_mesh,
317 Far::TopologyRefinerFactory<OsdMesh>::Options(Sdc::SCHEME_CATMARK, osd_mesh.sdc_options())));
318
319 /* adaptive refinement */
320 const bool has_fvar = osd_mesh.use_smooth_fvar();
321 const int max_isolation = 3; // TODO: get from Blender
322
323 Far::TopologyRefiner::AdaptiveOptions adaptive_options(max_isolation);
324 adaptive_options.considerFVarChannels = has_fvar;
325 adaptive_options.useInfSharpPatch = true;
326 refiner->RefineAdaptive(adaptive_options);
327
328 /* create patch table */
329 Far::PatchTableFactory::Options patch_options;
330 patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
331 patch_options.generateFVarTables = has_fvar;
332 patch_options.generateFVarLegacyLinearPatches = false;
333 patch_options.useInfSharpPatch = true;
334
335 patch_table.reset(Far::PatchTableFactory::Create(*refiner, patch_options));
336
337 /* interpolate verts */
338 const int num_refiner_verts = refiner->GetNumVerticesTotal();
339 const int num_local_points = patch_table->GetNumLocalPoints();
340 const int num_base_verts = osd_mesh.mesh.get_num_subd_base_verts();
341 const float3 *verts_data = osd_mesh.mesh.get_verts().data();
342
343 refined_verts.resize(num_refiner_verts + num_local_points);
344 for (int i = 0; i < num_base_verts; i++) {
345 refined_verts[i].value = verts_data[i];
346 }
347
348 OsdValue<float3> *src = refined_verts.data();
349 for (int i = 0; i < refiner->GetMaxLevel(); i++) {
350 OsdValue<float3> *dest = src + refiner->GetLevel(i).GetNumVertices();
351 Far::PrimvarRefiner(*refiner).Interpolate(i + 1, src, dest);
352 src = dest;
353 }
354
355 if (num_local_points) {
356 patch_table->ComputeLocalPointValues(refined_verts.data(), &refined_verts[num_refiner_verts]);
357 }
358
359 /* Create patch map */
360 patch_map = make_unique<Far::PatchMap>(*patch_table);
361}
362
363/* OsdPatch */
364
365void OsdPatch::eval(
366 float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, const float v) const
367{
368 const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
369 patch_index, (double)u, (double)v);
370
371 float p_weights[20], du_weights[20], dv_weights[20];
372 osd_data.patch_table->EvaluateBasis(handle, u, v, p_weights, du_weights, dv_weights);
373
374 const Far::ConstIndexArray cv = osd_data.patch_table->GetPatchVertices(handle);
375
376 if (P) {
377 *P = zero_float3();
378 }
379 float3 du = zero_float3();
380 float3 dv = zero_float3();
381
382 for (int i = 0; i < cv.size(); i++) {
383 const float3 p = osd_data.refined_verts[cv[i]].value;
384
385 if (P) {
386 *P += p * p_weights[i];
387 }
388 du += p * du_weights[i];
389 dv += p * dv_weights[i];
390 }
391
392 if (dPdu) {
393 *dPdu = du;
394 }
395 if (dPdv) {
396 *dPdv = dv;
397 }
398 if (N) {
399 *N = safe_normalize_fallback(cross(du, dv), make_float3(0.0f, 0.0f, 1.0f));
400 }
401}
402
404
405#endif
struct Mesh Mesh
ATTR_WARN_UNUSED_RESULT const BMVert * v
list< Attribute > attributes
CCL_NAMESPACE_BEGIN struct Options options
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
@ ATTR_STD_UV
@ ATTR_SUBDIVIDE_SMOOTH_FVAR
@ ATTR_ELEMENT_CORNER_BYTE
@ ATTR_ELEMENT_CORNER
#define LOG_WARNING
Definition log.h:103
ccl_device_inline float3 safe_normalize_fallback(const float3 a, const float3 fallback)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
static ulong * next
#define N
#define T
#define INDEX_INVALID
#define min(a, b)
Definition sort.cc:36
AttributeElement element
TypeDesc type
AttributeStandard std
static bool same_storage(const TypeDesc a, const TypeDesc b)
size_t get_num_subd_faces() const
Definition scene/mesh.h:235
size_t get_num_subd_base_verts() const
Definition scene/mesh.h:243
AttributeSet subd_attributes
Definition scene/mesh.h:174
@ SUBDIVISION_FVAR_LINEAR_NONE
Definition scene/mesh.h:130
@ SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS2
Definition scene/mesh.h:133
@ SUBDIVISION_FVAR_LINEAR_CORNERS_ONLY
Definition scene/mesh.h:131
@ SUBDIVISION_FVAR_LINEAR_ALL
Definition scene/mesh.h:135
@ SUBDIVISION_FVAR_LINEAR_BOUNDARIES
Definition scene/mesh.h:134
@ SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS1
Definition scene/mesh.h:132
SubdFace get_subd_face(const size_t index) const
SubdEdgeCrease get_subd_crease(const size_t i) const
Definition scene/mesh.h:106
@ SUBDIVISION_BOUNDARY_EDGE_ONLY
Definition scene/mesh.h:125
@ SUBDIVISION_BOUNDARY_EDGE_AND_CORNER
Definition scene/mesh.h:126
@ SUBDIVISION_BOUNDARY_NONE
Definition scene/mesh.h:124
ustring name
Definition graph/node.h:177
i
Definition text_draw.cc:230