Blender V4.3
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.h"
16#include "BKE_mesh.hh"
17#include "BKE_mesh_runtime.hh"
18
20#include "mesh.hh"
21
22namespace blender::io::hydra {
23
24namespace usdtokens {
25static const pxr::TfToken st("st", pxr::TfToken::Immortal);
26}
27
29 const Object *object,
30 pxr::SdfPath const &prim_id)
31 : ObjectData(scene_delegate, object, prim_id)
32{
33}
34
36{
37 ID_LOGN(1, "");
38
39 Object *object = (Object *)id;
40 Mesh *mesh = BKE_object_to_mesh(nullptr, object, false);
41 if (mesh) {
42 write_submeshes(mesh);
43 }
45
48}
49
51{
52 ID_LOGN(1, "");
53 update_prims();
54}
55
57{
58 ID_LOG(1, "");
59 submeshes_.clear();
60 update_prims();
61}
62
64{
65 Object *object = (Object *)id;
66 if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
67 init();
68 update_prims();
69 return;
70 }
71
72 pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
73 if (id->recalc & ID_RECALC_SHADING) {
75 bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
76 }
77 if (id->recalc & ID_RECALC_TRANSFORM) {
79 bits |= pxr::HdChangeTracker::DirtyTransform;
80 }
81
82 if (bits == pxr::HdChangeTracker::Clean) {
83 return;
84 }
85
86 for (int i = 0; i < submeshes_.size(); ++i) {
87 scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(submesh_prim_id(i), bits);
88 ID_LOGN(1, "%d", i);
89 }
90}
91
92pxr::VtValue MeshData::get_data(pxr::TfToken const & /*key*/) const
93{
94 return pxr::VtValue();
95}
96
97pxr::VtValue MeshData::get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const
98{
99 if (key == pxr::HdTokens->normals) {
100 return pxr::VtValue(submesh(id).normals);
101 }
102 if (key == usdtokens::st) {
103 return pxr::VtValue(submesh(id).uvs);
104 }
105 if (key == pxr::HdTokens->points) {
106 return pxr::VtValue(submesh(id).vertices);
107 }
108
109 return get_data(key);
110}
111
112pxr::SdfPath MeshData::material_id(pxr::SdfPath const &id) const
113{
114 const SubMesh &sm = submesh(id);
115 if (!sm.mat_data) {
116 return pxr::SdfPath();
117 }
118 return sm.mat_data->prim_id;
119}
120
122{
123 for (auto &sm : submeshes_) {
124 if (sm.mat_data && !sm.mat_data->prim_id.IsEmpty()) {
125 paths.add(sm.mat_data->prim_id);
126 }
127 }
128}
129
130pxr::HdMeshTopology MeshData::topology(pxr::SdfPath const &id) const
131{
132 const SubMesh &sm = submesh(id);
133 return pxr::HdMeshTopology(pxr::PxOsdOpenSubdivTokens->none,
134 pxr::HdTokens->rightHanded,
137}
138
139pxr::HdPrimvarDescriptorVector MeshData::primvar_descriptors(
140 pxr::HdInterpolation interpolation) const
141{
142 pxr::HdPrimvarDescriptorVector primvars;
143 if (interpolation == pxr::HdInterpolationVertex) {
144 primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
145 }
146 else if (interpolation == pxr::HdInterpolationFaceVarying) {
147 if (!submeshes_[0].normals.empty()) {
148 primvars.emplace_back(
149 pxr::HdTokens->normals, interpolation, pxr::HdPrimvarRoleTokens->normal);
150 }
151 if (!submeshes_[0].uvs.empty()) {
152 primvars.emplace_back(
153 usdtokens::st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
154 }
155 }
156 return primvars;
157}
158
159pxr::HdCullStyle MeshData::cull_style(pxr::SdfPath const &id) const
160{
161 const SubMesh &sm = submesh(id);
162 if (sm.mat_data) {
163 return sm.mat_data->cull_style();
164 }
165 return pxr::HdCullStyle::HdCullStyleNothing;
166}
167
168bool MeshData::double_sided(pxr::SdfPath const &id) const
169{
170 const SubMesh &sm = submesh(id);
171 if (sm.mat_data) {
172 return sm.mat_data->double_sided;
173 }
174 return true;
175}
176
178{
179 for (int i = 0; i < submeshes_.size(); ++i) {
180 if (submeshes_[i].mat_data == mat_data) {
181 scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(
182 submesh_prim_id(i),
183 pxr::HdChangeTracker::DirtyDoubleSided | pxr::HdChangeTracker::DirtyCullStyle);
184 ID_LOGN(1, "%d", i);
185 }
186 }
187}
188
189pxr::SdfPathVector MeshData::submesh_paths() const
190{
191 pxr::SdfPathVector ret;
192 for (int i = 0; i < submeshes_.size(); ++i) {
193 ret.push_back(submesh_prim_id(i));
194 }
195 return ret;
196}
197
199{
200 const Object *object = (const Object *)id;
201 for (int i = 0; i < submeshes_.size(); ++i) {
202 SubMesh &m = submeshes_[i];
203 const Material *mat = BKE_object_material_get_eval(const_cast<Object *>(object),
204 m.mat_index + 1);
206 }
207}
208
209pxr::SdfPath MeshData::submesh_prim_id(int index) const
210{
211 char name[16];
212 SNPRINTF(name, "SM_%04d", index);
213 return prim_id.AppendElementString(name);
214}
215
216const MeshData::SubMesh &MeshData::submesh(pxr::SdfPath const &id) const
217{
218 int index;
219 sscanf(id.GetName().c_str(), "SM_%d", &index);
220 return submeshes_[index];
221}
222
229template<typename T> static void resize_uninitialized(pxr::VtArray<T> &array, const int new_size)
230{
231 static_assert(std::is_trivial_v<T>);
232 array.resize(new_size, [](auto /*begin*/, auto /*end*/) {});
233}
234
235static std::pair<bke::MeshNormalDomain, Span<float3>> get_mesh_normals(const Mesh &mesh)
236{
237 switch (mesh.normals_domain()) {
239 return {bke::MeshNormalDomain::Face, mesh.face_normals()};
241 return {bke::MeshNormalDomain::Point, mesh.vert_normals()};
243 return {bke::MeshNormalDomain::Corner, mesh.corner_normals()};
244 }
246 return {};
247}
248
249template<typename T>
251 const bool copy_all_verts,
252 const Span<T> src_data,
253 MutableSpan<T> dst_data)
254{
255 if (copy_all_verts) {
256 array_utils::copy(src_data, dst_data);
257 }
258 else {
259 array_utils::gather(src_data, verts, dst_data);
260 }
261}
262
263template<typename T>
264void gather_face_data(const Span<int> tri_faces,
265 const IndexMask &triangles,
266 const Span<T> src_data,
267 MutableSpan<T> dst_data)
268{
269 triangles.foreach_index_optimized<int>(GrainSize(1024), [&](const int src, const int dst) {
270 dst_data[dst] = src_data[tri_faces[src]];
271 });
272}
273
274template<typename T>
275void gather_corner_data(const Span<int3> corner_tris,
276 const IndexMask &triangles,
277 const Span<T> src_data,
278 MutableSpan<T> dst_data)
279{
280 triangles.foreach_index_optimized<int>(GrainSize(1024), [&](const int src, const int dst) {
281 const int3 &tri = corner_tris[src];
282 dst_data[dst * 3 + 0] = src_data[tri[0]];
283 dst_data[dst * 3 + 1] = src_data[tri[1]];
284 dst_data[dst * 3 + 2] = src_data[tri[2]];
285 });
286}
287
288static void copy_submesh(const Mesh &mesh,
289 const Span<float3> vert_positions,
290 const Span<int> corner_verts,
291 const Span<int3> corner_tris,
292 const Span<int> tri_faces,
293 const std::pair<bke::MeshNormalDomain, Span<float3>> normals,
294 const Span<float2> uv_map,
295 const IndexMask &triangles,
297{
298 resize_uninitialized(sm.face_vertex_indices, triangles.size() * 3);
299
300 /* If all triangles are part of this submesh and there are no loose vertices that shouldn't be
301 * copied (Hydra will warn about this), vertex index compression can be completely skipped. */
302 const bool copy_all_verts = triangles.size() == corner_tris.size() &&
303 mesh.verts_no_face().count == 0;
304
305 int dst_verts_num;
307 if (copy_all_verts) {
309 corner_verts,
310 corner_tris,
312 dst_verts_num = vert_positions.size();
313 }
314 else {
315 /* Compress vertex indices to be contiguous so it's only necessary to copy values
316 * for vertices actually used by the subset of triangles. */
317 verts.reserve(triangles.size());
318 triangles.foreach_index([&](const int src, const int dst) {
319 const int3 &tri = corner_tris[src];
320 sm.face_vertex_indices[dst * 3 + 0] = verts.index_of_or_add(corner_verts[tri[0]]);
321 sm.face_vertex_indices[dst * 3 + 1] = verts.index_of_or_add(corner_verts[tri[1]]);
322 sm.face_vertex_indices[dst * 3 + 2] = verts.index_of_or_add(corner_verts[tri[2]]);
323 });
324 dst_verts_num = verts.size();
325 }
326
327 resize_uninitialized(sm.vertices, dst_verts_num);
329 copy_all_verts,
330 vert_positions,
331 MutableSpan(sm.vertices.data(), sm.vertices.size()).cast<float3>());
332
333 resize_uninitialized(sm.face_vertex_counts, triangles.size());
334 std::fill(sm.face_vertex_counts.begin(), sm.face_vertex_counts.end(), 3);
335
336 const Span<float3> src_normals = normals.second;
337 resize_uninitialized(sm.normals, triangles.size() * 3);
338 MutableSpan dst_normals = MutableSpan(sm.normals.data(), sm.normals.size()).cast<float3>();
339 switch (normals.first) {
341 triangles.foreach_index(GrainSize(1024), [&](const int src, const int dst) {
342 std::fill_n(&dst_normals[dst * 3], 3, src_normals[tri_faces[src]]);
343 });
344 break;
346 triangles.foreach_index(GrainSize(1024), [&](const int src, const int dst) {
347 const int3 &tri = corner_tris[src];
348 dst_normals[dst * 3 + 0] = src_normals[corner_verts[tri[0]]];
349 dst_normals[dst * 3 + 1] = src_normals[corner_verts[tri[1]]];
350 dst_normals[dst * 3 + 2] = src_normals[corner_verts[tri[2]]];
351 });
352 break;
354 gather_corner_data(corner_tris, triangles, src_normals, dst_normals);
355 break;
356 }
357
358 if (!uv_map.is_empty()) {
359 resize_uninitialized(sm.uvs, triangles.size() * 3);
361 corner_tris, triangles, uv_map, MutableSpan(sm.uvs.data(), sm.uvs.size()).cast<float2>());
362 }
363}
364
365void MeshData::write_submeshes(const Mesh *mesh)
366{
367 const int mat_count = BKE_object_material_count_eval(reinterpret_cast<const Object *>(id));
368 submeshes_.reinitialize(mat_count > 0 ? mat_count : 1);
369 for (const int i : submeshes_.index_range()) {
370 submeshes_[i].mat_index = i;
371 }
372
373 const Span<float3> vert_positions = mesh->vert_positions();
374 const Span<int> corner_verts = mesh->corner_verts();
375 const Span<int3> corner_tris = mesh->corner_tris();
376 const Span<int> tri_faces = mesh->corner_tri_faces();
377 const std::pair<bke::MeshNormalDomain, Span<float3>> normals = get_mesh_normals(*mesh);
378 const bke::AttributeAccessor attributes = mesh->attributes();
379 const StringRef active_uv = CustomData_get_active_layer_name(&mesh->corner_data, CD_PROP_FLOAT2);
380 const VArraySpan uv_map = *attributes.lookup<float2>(active_uv, bke::AttrDomain::Corner);
381 const VArraySpan material_indices = *attributes.lookup<int>("material_index",
383
384 if (material_indices.is_empty()) {
385 copy_submesh(*mesh,
386 vert_positions,
387 corner_verts,
388 corner_tris,
389 tri_faces,
390 normals,
391 uv_map,
392 corner_tris.index_range(),
393 submeshes_.first());
394 return;
395 }
396
397 IndexMaskMemory memory;
398 Array<IndexMask> triangles_by_material(submeshes_.size());
399 const int max_index = std::max(mat_count - 1, 0);
401 corner_tris.index_range(),
402 memory,
403 [&](const int i) { return std::clamp(material_indices[tri_faces[i]], 0, max_index); },
404 triangles_by_material);
405
406 threading::parallel_for(submeshes_.index_range(), 1, [&](const IndexRange range) {
407 for (const int i : range) {
408 copy_submesh(*mesh,
409 vert_positions,
410 corner_verts,
411 corner_tris,
412 tri_faces,
413 normals,
414 uv_map,
415 triangles_by_material[i],
416 submeshes_[i]);
417 }
418 });
419
420 /* Remove submeshes without faces */
421 submeshes_.remove_if([](const SubMesh &submesh) { return submesh.face_vertex_counts.empty(); });
422}
423
424void MeshData::update_prims()
425{
426 auto &render_index = scene_delegate_->GetRenderIndex();
427 int i;
428 for (i = 0; i < submeshes_.size(); ++i) {
429 pxr::SdfPath p = submesh_prim_id(i);
430 if (i < submeshes_count_) {
431 render_index.GetChangeTracker().MarkRprimDirty(p, pxr::HdChangeTracker::AllDirty);
432 ID_LOGN(1, "Update %d", i);
433 }
434 else {
435 render_index.InsertRprim(pxr::HdPrimTypeTokens->mesh, scene_delegate_, p);
436 ID_LOGN(1, "Insert %d", i);
437 }
438 }
439 for (; i < submeshes_count_; ++i) {
440 render_index.RemoveRprim(submesh_prim_id(i));
441 ID_LOG(1, "Remove %d", i);
442 }
443 submeshes_count_ = submeshes_.size();
444}
445
446} // 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 struct Object *ob)
struct Material * BKE_object_material_get_eval(struct 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:97
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SHADING
Definition DNA_ID.h:1061
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ CD_PROP_FLOAT2
T * resize(size_t newsize)
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:736
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
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)
static float verts[][3]
#define ID_LOG(level, msg,...)
Definition id.hh:53
#define ID_LOGN(level, msg,...)
Definition id.hh:56
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:95
return ret
Definition DNA_ID.h:413
pxr::VtIntArray face_vertex_counts
Definition mesh.hh:23
pxr::VtIntArray face_vertex_indices
Definition mesh.hh:24