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,
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
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();
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
AttributeSet attributes
T * resize(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:736
bool add(const Key &key)
Definition BLI_set.hh:248
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
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_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
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
return ret
Definition DNA_ID.h:413
CustomData corner_data
pxr::VtIntArray face_vertex_counts
Definition mesh.hh:23
pxr::VtIntArray face_vertex_indices
Definition mesh.hh:24