Blender V5.0
io/usd/hydra/mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <pxr/base/gf/vec2f.h>
6#include <pxr/base/tf/staticTokens.h>
7#include <pxr/imaging/hd/tokens.h>
8
9#include "BLI_array_utils.hh"
10#include "BLI_string.h"
11#include "BLI_vector_set.hh"
12
13#include "BKE_attribute.hh"
14#include "BKE_customdata.hh"
15#include "BKE_material.hh"
16#include "BKE_mesh.hh"
17
19#include "mesh.hh"
20
21namespace blender::io::hydra {
22
23namespace usdtokens {
24static const pxr::TfToken st("st", pxr::TfToken::Immortal);
25}
26
28 const Object *object,
29 pxr::SdfPath const &prim_id)
30 : ObjectData(scene_delegate, object, prim_id)
31{
32}
33
35{
36 ID_LOGN("");
37
38 Object *object = (Object *)id;
39 Mesh *mesh = BKE_object_to_mesh(nullptr, object, false);
40 if (mesh) {
41 write_submeshes(mesh);
42 }
44
47}
48
50{
51 ID_LOGN("");
52 update_prims();
53}
54
56{
57 ID_LOG("");
58 submeshes_.clear();
59 update_prims();
60}
61
63{
64 Object *object = (Object *)id;
65 if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
66 init();
67 update_prims();
68 return;
69 }
70
71 pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
72 if (id->recalc & ID_RECALC_SHADING) {
74 bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
75 }
76 if (id->recalc & ID_RECALC_TRANSFORM) {
78 bits |= pxr::HdChangeTracker::DirtyTransform;
79 }
80
81 if (bits == pxr::HdChangeTracker::Clean) {
82 return;
83 }
84
85 for (int i = 0; i < submeshes_.size(); ++i) {
86 scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(submesh_prim_id(i), bits);
87 ID_LOGN("%d", i);
88 }
89}
90
91pxr::VtValue MeshData::get_data(pxr::TfToken const & /*key*/) const
92{
93 return pxr::VtValue();
94}
95
96pxr::VtValue MeshData::get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const
97{
98 if (key == pxr::HdTokens->normals) {
99 return pxr::VtValue(submesh(id).normals);
100 }
101 if (key == usdtokens::st) {
102 return pxr::VtValue(submesh(id).uvs);
103 }
104 if (key == pxr::HdTokens->points) {
105 return pxr::VtValue(submesh(id).vertices);
106 }
107
108 return get_data(key);
109}
110
111pxr::SdfPath MeshData::material_id(pxr::SdfPath const &id) const
112{
113 const SubMesh &sm = submesh(id);
114 if (!sm.mat_data) {
115 return pxr::SdfPath();
116 }
117 return sm.mat_data->prim_id;
118}
119
121{
122 for (const auto &sm : submeshes_) {
123 if (sm.mat_data && !sm.mat_data->prim_id.IsEmpty()) {
124 paths.add(sm.mat_data->prim_id);
125 }
126 }
127}
128
129pxr::HdMeshTopology MeshData::topology(pxr::SdfPath const &id) const
130{
131 const SubMesh &sm = submesh(id);
132 return pxr::HdMeshTopology(pxr::PxOsdOpenSubdivTokens->none,
133 pxr::HdTokens->rightHanded,
136}
137
138pxr::HdPrimvarDescriptorVector MeshData::primvar_descriptors(
139 pxr::HdInterpolation interpolation) const
140{
141 pxr::HdPrimvarDescriptorVector primvars;
142 if (interpolation == pxr::HdInterpolationVertex) {
143 primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
144 }
145 else if (interpolation == pxr::HdInterpolationFaceVarying) {
146 if (!submeshes_[0].normals.empty()) {
147 primvars.emplace_back(
148 pxr::HdTokens->normals, interpolation, pxr::HdPrimvarRoleTokens->normal);
149 }
150 if (!submeshes_[0].uvs.empty()) {
151 primvars.emplace_back(
152 usdtokens::st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
153 }
154 }
155 return primvars;
156}
157
158pxr::HdCullStyle MeshData::cull_style(pxr::SdfPath const &id) const
159{
160 const SubMesh &sm = submesh(id);
161 if (sm.mat_data) {
162 return sm.mat_data->cull_style();
163 }
164 return pxr::HdCullStyle::HdCullStyleNothing;
165}
166
167bool MeshData::double_sided(pxr::SdfPath const &id) const
168{
169 const SubMesh &sm = submesh(id);
170 if (sm.mat_data) {
171 return sm.mat_data->double_sided;
172 }
173 return true;
174}
175
177{
178 for (int i = 0; i < submeshes_.size(); ++i) {
179 if (submeshes_[i].mat_data == mat_data) {
180 scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(
181 submesh_prim_id(i),
182 pxr::HdChangeTracker::DirtyDoubleSided | pxr::HdChangeTracker::DirtyCullStyle);
183 ID_LOGN("%d", i);
184 }
185 }
186}
187
188pxr::SdfPathVector MeshData::submesh_paths() const
189{
190 pxr::SdfPathVector ret;
191 for (int i = 0; i < submeshes_.size(); ++i) {
192 ret.push_back(submesh_prim_id(i));
193 }
194 return ret;
195}
196
198{
199 const Object *object = (const Object *)id;
200 for (int i = 0; i < submeshes_.size(); ++i) {
201 SubMesh &m = submeshes_[i];
202 const Material *mat = BKE_object_material_get_eval(const_cast<Object *>(object),
203 m.mat_index + 1);
205 }
206}
207
208pxr::SdfPath MeshData::submesh_prim_id(int index) const
209{
210 char name[16];
211 SNPRINTF(name, "SM_%04d", index);
212 return prim_id.AppendElementString(name);
213}
214
215const MeshData::SubMesh &MeshData::submesh(pxr::SdfPath const &id) const
216{
217 int index;
218 sscanf(id.GetName().c_str(), "SM_%d", &index);
219 return submeshes_[index];
220}
221
228template<typename T> static void resize_uninitialized(pxr::VtArray<T> &array, const int new_size)
229{
230 static_assert(std::is_trivial_v<T>);
231 array.resize(new_size, [](auto /*begin*/, auto /*end*/) {});
232}
233
234static std::pair<bke::MeshNormalDomain, Span<float3>> get_mesh_normals(const Mesh &mesh)
235{
236 switch (mesh.normals_domain()) {
238 return {bke::MeshNormalDomain::Face, mesh.face_normals()};
240 return {bke::MeshNormalDomain::Point, mesh.vert_normals()};
242 return {bke::MeshNormalDomain::Corner, mesh.corner_normals()};
243 }
245 return {};
246}
247
248template<typename T>
250 const bool copy_all_verts,
251 const Span<T> src_data,
252 MutableSpan<T> dst_data)
253{
254 if (copy_all_verts) {
255 array_utils::copy(src_data, dst_data);
256 }
257 else {
258 array_utils::gather(src_data, verts, dst_data);
259 }
260}
261
262template<typename T>
263void gather_face_data(const Span<int> tri_faces,
264 const IndexMask &triangles,
265 const Span<T> src_data,
266 MutableSpan<T> dst_data)
267{
268 triangles.foreach_index_optimized<int>(GrainSize(1024), [&](const int src, const int dst) {
269 dst_data[dst] = src_data[tri_faces[src]];
270 });
271}
272
273template<typename T>
274void gather_corner_data(const Span<int3> corner_tris,
275 const IndexMask &triangles,
276 const Span<T> src_data,
277 MutableSpan<T> dst_data)
278{
279 triangles.foreach_index_optimized<int>(GrainSize(1024), [&](const int src, const int dst) {
280 const int3 &tri = corner_tris[src];
281 dst_data[dst * 3 + 0] = src_data[tri[0]];
282 dst_data[dst * 3 + 1] = src_data[tri[1]];
283 dst_data[dst * 3 + 2] = src_data[tri[2]];
284 });
285}
286
287static void copy_submesh(const Mesh &mesh,
288 const Span<float3> vert_positions,
289 const Span<int> corner_verts,
290 const Span<int3> corner_tris,
291 const Span<int> tri_faces,
293 const Span<float2> uv_map,
294 const IndexMask &triangles,
296{
297 resize_uninitialized(sm.face_vertex_indices, triangles.size() * 3);
298
299 /* If all triangles are part of this submesh and there are no loose vertices that shouldn't be
300 * copied (Hydra will warn about this), vertex index compression can be completely skipped. */
301 const bool copy_all_verts = triangles.size() == corner_tris.size() &&
302 mesh.verts_no_face().count == 0;
303
304 int dst_verts_num;
306 if (copy_all_verts) {
308 corner_verts,
309 corner_tris,
311 dst_verts_num = vert_positions.size();
312 }
313 else {
314 /* Compress vertex indices to be contiguous so it's only necessary to copy values
315 * for vertices actually used by the subset of triangles. */
316 verts.reserve(triangles.size());
317 triangles.foreach_index([&](const int src, const int dst) {
318 const int3 &tri = corner_tris[src];
319 sm.face_vertex_indices[dst * 3 + 0] = verts.index_of_or_add(corner_verts[tri[0]]);
320 sm.face_vertex_indices[dst * 3 + 1] = verts.index_of_or_add(corner_verts[tri[1]]);
321 sm.face_vertex_indices[dst * 3 + 2] = verts.index_of_or_add(corner_verts[tri[2]]);
322 });
323 dst_verts_num = verts.size();
324 }
325
326 resize_uninitialized(sm.vertices, dst_verts_num);
328 copy_all_verts,
329 vert_positions,
330 MutableSpan(sm.vertices.data(), sm.vertices.size()).cast<float3>());
331
333 std::fill(sm.face_vertex_counts.begin(), sm.face_vertex_counts.end(), 3);
334
335 const Span<float3> src_normals = normals.second;
336 resize_uninitialized(sm.normals, triangles.size() * 3);
337 MutableSpan dst_normals = MutableSpan(sm.normals.data(), sm.normals.size()).cast<float3>();
338 switch (normals.first) {
340 triangles.foreach_index(GrainSize(1024), [&](const int src, const int dst) {
341 std::fill_n(&dst_normals[dst * 3], 3, src_normals[tri_faces[src]]);
342 });
343 break;
345 triangles.foreach_index(GrainSize(1024), [&](const int src, const int dst) {
346 const int3 &tri = corner_tris[src];
347 dst_normals[dst * 3 + 0] = src_normals[corner_verts[tri[0]]];
348 dst_normals[dst * 3 + 1] = src_normals[corner_verts[tri[1]]];
349 dst_normals[dst * 3 + 2] = src_normals[corner_verts[tri[2]]];
350 });
351 break;
353 gather_corner_data(corner_tris, triangles, src_normals, dst_normals);
354 break;
355 }
356
357 if (!uv_map.is_empty()) {
358 resize_uninitialized(sm.uvs, triangles.size() * 3);
360 corner_tris, triangles, uv_map, MutableSpan(sm.uvs.data(), sm.uvs.size()).cast<float2>());
361 }
362}
363
364void MeshData::write_submeshes(const Mesh *mesh)
365{
366 const int mat_count = BKE_object_material_count_eval(reinterpret_cast<const Object *>(id));
367 submeshes_.reinitialize(mat_count > 0 ? mat_count : 1);
368 for (const int i : submeshes_.index_range()) {
369 submeshes_[i].mat_index = i;
370 }
371
372 const Span<float3> vert_positions = mesh->vert_positions();
373 const Span<int> corner_verts = mesh->corner_verts();
374 const Span<int3> corner_tris = mesh->corner_tris();
375 const Span<int> tri_faces = mesh->corner_tri_faces();
376 const std::pair<bke::MeshNormalDomain, Span<float3>> normals = get_mesh_normals(*mesh);
377 const bke::AttributeAccessor attributes = mesh->attributes();
379 const VArraySpan uv_map = *attributes.lookup<float2>(active_uv, bke::AttrDomain::Corner);
380 const VArraySpan material_indices = *attributes.lookup<int>("material_index",
382
383 if (material_indices.is_empty()) {
384 copy_submesh(*mesh,
385 vert_positions,
386 corner_verts,
387 corner_tris,
388 tri_faces,
389 normals,
390 uv_map,
391 corner_tris.index_range(),
392 submeshes_.first());
393 return;
394 }
395
396 IndexMaskMemory memory;
397 Array<IndexMask> triangles_by_material(submeshes_.size());
398 const int max_index = std::max(mat_count - 1, 0);
400 corner_tris.index_range(),
401 memory,
402 [&](const int i) { return std::clamp(material_indices[tri_faces[i]], 0, max_index); },
403 triangles_by_material);
404
405 threading::parallel_for(submeshes_.index_range(), 1, [&](const IndexRange range) {
406 for (const int i : range) {
407 copy_submesh(*mesh,
408 vert_positions,
409 corner_verts,
410 corner_tris,
411 tri_faces,
412 normals,
413 uv_map,
414 triangles_by_material[i],
415 submeshes_[i]);
416 }
417 });
418
419 /* Remove submeshes without faces */
420 submeshes_.remove_if([](const SubMesh &submesh) { return submesh.face_vertex_counts.empty(); });
421}
422
423void MeshData::update_prims()
424{
425 auto &render_index = scene_delegate_->GetRenderIndex();
426 int i;
427 for (i = 0; i < submeshes_.size(); ++i) {
428 pxr::SdfPath p = submesh_prim_id(i);
429 if (i < submeshes_count_) {
430 render_index.GetChangeTracker().MarkRprimDirty(p, pxr::HdChangeTracker::AllDirty);
431 ID_LOGN("Update %d", i);
432 }
433 else {
434 render_index.InsertRprim(pxr::HdPrimTypeTokens->mesh, scene_delegate_, p);
435 ID_LOGN("Insert %d", i);
436 }
437 }
438 for (; i < submeshes_count_; ++i) {
439 render_index.RemoveRprim(submesh_prim_id(i));
440 ID_LOG("Remove %d", i);
441 }
442 submeshes_count_ = submeshes_.size();
443}
444
445} // namespace blender::io::hydra
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
General operations, lookup, etc. for materials.
int BKE_object_material_count_eval(const Object *ob)
Material * BKE_object_material_get_eval(Object *ob, short act)
void BKE_object_to_mesh_clear(Object *object)
Mesh * BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SHADING
Definition DNA_ID.h:1094
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CD_PROP_FLOAT2
AttributeSet attributes
T * resize(const size_t newsize)
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:749
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup(const StringRef attribute_id) const
void foreach_index_optimized(Fn &&fn) const
void foreach_index(Fn &&fn) const
pxr::SdfPath prim_id
Definition id.hh:36
HydraSceneDelegate * scene_delegate_
Definition id.hh:39
pxr::HdMeshTopology topology(pxr::SdfPath const &id) const
pxr::VtValue get_data(pxr::TfToken const &key) const override
pxr::SdfPathVector submesh_paths() const
pxr::HdCullStyle cull_style(pxr::SdfPath const &id) const
void update_double_sided(MaterialData *mat_data)
void available_materials(Set< pxr::SdfPath > &paths) const override
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const
bool double_sided(pxr::SdfPath const &id) const
MeshData(HydraSceneDelegate *scene_delegate, const Object *object, pxr::SdfPath const &prim_id)
virtual pxr::SdfPath material_id() const
MaterialData * get_or_create_material(const Material *mat)
ObjectData(HydraSceneDelegate *scene_delegate, const Object *object, pxr::SdfPath const &prim_id)
static float verts[][3]
static float normals[][3]
#define ID_LOGN(msg,...)
Definition id.hh:55
#define ID_LOG(msg,...)
Definition id.hh:53
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void vert_tris_from_corner_tris(Span< int > corner_verts, Span< int3 > corner_tris, MutableSpan< int3 > vert_tris)
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
static std::pair< bke::MeshNormalDomain, Span< float3 > > get_mesh_normals(const Mesh &mesh)
static void resize_uninitialized(pxr::VtArray< T > &array, const int new_size)
void gather_vert_data(const Span< int > verts, const bool copy_all_verts, const Span< T > src_data, MutableSpan< T > dst_data)
static void copy_submesh(const Mesh &mesh, const Span< float3 > vert_positions, const Span< int > corner_verts, const Span< int3 > corner_tris, const Span< int > tri_faces, const std::pair< bke::MeshNormalDomain, Span< float3 > > normals, const Span< float2 > uv_map, const IndexMask &triangles, MeshData::SubMesh &sm)
void gather_face_data(const Span< int > tri_faces, const IndexMask &triangles, const Span< T > src_data, MutableSpan< T > dst_data)
void gather_corner_data(const Span< int3 > corner_tris, const IndexMask &triangles, const Span< T > src_data, MutableSpan< T > dst_data)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
const char * name
return ret
Definition DNA_ID.h:414
CustomData corner_data
pxr::VtIntArray face_vertex_counts
Definition mesh.hh:21
pxr::VtIntArray face_vertex_indices
Definition mesh.hh:22
i
Definition text_draw.cc:230