Blender V4.3
instancer.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 "instancer.hh"
6
7#include <pxr/base/gf/vec2f.h>
8#include <pxr/imaging/hd/light.h>
9
10#include "BKE_particle.h"
11
12#include "BLI_math_matrix.h"
13#include "BLI_string.h"
14
16
17#include "DNA_particle_types.h"
18
20
21namespace blender::io::hydra {
22
23InstancerData::InstancerData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id)
24 : IdData(scene_delegate, nullptr, prim_id)
25{
26}
27
29
31
33{
34 CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", prim_id.GetText());
35 for (auto &m_inst : mesh_instances_.values()) {
36 m_inst.data->remove();
37 }
38 if (!mesh_instances_.is_empty()) {
39 scene_delegate_->GetRenderIndex().RemoveInstancer(prim_id);
40 }
41 mesh_instances_.clear();
42
43 for (auto &l_inst : nonmesh_instances_.values()) {
44 l_inst.transforms.clear();
45 update_nonmesh_instance(l_inst);
46 }
47 nonmesh_instances_.clear();
48}
49
51
52pxr::VtValue InstancerData::get_data(pxr::TfToken const &key) const
53{
54 ID_LOG(3, "%s", key.GetText());
55 if (key == pxr::HdInstancerTokens->instanceTransforms) {
56 return pxr::VtValue(mesh_transforms_);
57 }
58 return pxr::VtValue();
59}
60
61pxr::GfMatrix4d InstancerData::transform(pxr::SdfPath const &id) const
62{
63 NonmeshInstance *nm_inst = nonmesh_instance(id);
64 if (nm_inst) {
65 return nm_inst->transforms[nonmesh_prim_id_index(id)];
66 }
67
68 /* Mesh instance transform must be identity */
69 return pxr::GfMatrix4d(1.0);
70}
71
72pxr::HdPrimvarDescriptorVector InstancerData::primvar_descriptors(
73 pxr::HdInterpolation interpolation) const
74{
75 pxr::HdPrimvarDescriptorVector primvars;
76 if (interpolation == pxr::HdInterpolationInstance) {
77 primvars.emplace_back(
78 pxr::HdInstancerTokens->instanceTransforms, interpolation, pxr::HdPrimvarRoleTokens->none);
79 }
80 return primvars;
81}
82
83pxr::VtIntArray InstancerData::indices(pxr::SdfPath const &id) const
84{
85 return mesh_instance(id)->indices;
86}
87
88ObjectData *InstancerData::object_data(pxr::SdfPath const &id) const
89{
90 MeshInstance *m_inst = mesh_instance(id);
91 if (m_inst) {
92 return m_inst->data.get();
93 }
94 NonmeshInstance *nm_inst = nonmesh_instance(id);
95 if (nm_inst) {
96 return nm_inst->data.get();
97 }
98 return nullptr;
99}
100
101pxr::SdfPathVector InstancerData::prototypes() const
102{
103 pxr::SdfPathVector paths;
104 for (auto &m_inst : mesh_instances_.values()) {
105 for (auto &p : m_inst.data->submesh_paths()) {
106 paths.push_back(p);
107 }
108 }
109 return paths;
110}
111
113{
114 for (auto &m_inst : mesh_instances_.values()) {
115 m_inst.data->available_materials(paths);
116 }
117 for (auto &l_inst : nonmesh_instances_.values()) {
118 l_inst.data->available_materials(paths);
119 }
120}
121
123{
124 for (auto &m_inst : mesh_instances_.values()) {
125 m_inst.data->update_double_sided(mat_data);
126 }
127}
128
130{
131 mesh_transforms_.clear();
132 for (auto &m_inst : mesh_instances_.values()) {
133 m_inst.indices.clear();
134 }
135 for (auto &l_inst : nonmesh_instances_.values()) {
136 l_inst.transforms.clear();
137 }
138}
139
141{
142 Object *object = dupli->ob;
143 pxr::SdfPath p_id = object_prim_id(object);
144 if (ObjectData::is_mesh(object)) {
145 MeshInstance *m_inst = mesh_instance(p_id);
146 if (!m_inst) {
147 m_inst = &mesh_instances_.lookup_or_add_default(p_id);
148 m_inst->data = std::make_unique<MeshData>(scene_delegate_, object, p_id);
149 m_inst->data->init();
150 m_inst->data->insert();
151 }
152 else {
153 m_inst->data->update();
154 }
155 ID_LOG(2, "Mesh %s %d", m_inst->data->id->name, int(mesh_transforms_.size()));
156 m_inst->indices.push_back(mesh_transforms_.size());
157 mesh_transforms_.push_back(gf_matrix_from_transform(dupli->mat));
158 }
159 else {
160 NonmeshInstance *nm_inst = nonmesh_instance(p_id);
161 if (!nm_inst) {
162 nm_inst = &nonmesh_instances_.lookup_or_add_default(p_id);
163 nm_inst->data = ObjectData::create(scene_delegate_, object, p_id);
164 }
165 ID_LOG(2, "Nonmesh %s %d", nm_inst->data->id->name, int(nm_inst->transforms.size()));
166 nm_inst->transforms.push_back(gf_matrix_from_transform(dupli->mat));
167 }
168
169 LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
171 continue;
172 }
173 if (HairData::is_supported(psys) && HairData::is_visible(scene_delegate_, object, psys)) {
174 pxr::SdfPath h_id = hair_prim_id(object, psys);
175 NonmeshInstance *nm_inst = nonmesh_instance(h_id);
176 if (!nm_inst) {
177 nm_inst = &nonmesh_instances_.lookup_or_add_default(h_id);
178 nm_inst->data = std::make_unique<HairData>(scene_delegate_, object, h_id, psys);
179 nm_inst->data->init();
180 }
181 ID_LOG(2, "Nonmesh %s %d", nm_inst->data->id->name, int(nm_inst->transforms.size()));
182 nm_inst->transforms.push_back(gf_matrix_from_transform(psys->imat) *
184 }
185 }
186}
187
189{
190 /* Remove mesh instances without indices. */
191 mesh_instances_.remove_if([&](auto item) {
192 bool res = item.value.indices.empty();
193 if (res) {
194 item.value.data->remove();
195 }
196 return res;
197 });
198
199 /* Update light instances and remove instances without transforms. */
200 for (auto &l_inst : nonmesh_instances_.values()) {
201 update_nonmesh_instance(l_inst);
202 }
203 nonmesh_instances_.remove_if([&](auto item) { return item.value.transforms.empty(); });
204
205 /* Insert/remove/update instancer in RenderIndex. */
206 pxr::HdRenderIndex &index = scene_delegate_->GetRenderIndex();
207 if (mesh_instances_.is_empty()) {
208 /* Important: removing instancer when nonmesh_instances_ are empty too */
209 if (index.HasInstancer(prim_id) && nonmesh_instances_.is_empty()) {
210 index.RemoveInstancer(prim_id);
211 ID_LOG(1, "Remove instancer");
212 }
213 }
214 else {
215 if (index.HasInstancer(prim_id)) {
216 index.GetChangeTracker().MarkInstancerDirty(prim_id, pxr::HdChangeTracker::AllDirty);
217 ID_LOG(1, "Update instancer");
218 }
219 else {
220 index.InsertInstancer(scene_delegate_, prim_id);
221 ID_LOG(1, "Insert instancer");
222 }
223 }
224}
225
226pxr::SdfPath InstancerData::object_prim_id(Object *object) const
227{
228 /* Making id of object in form like <prefix>_<pointer in 16 hex digits format> */
229 char name[32];
230 SNPRINTF(name, "O_%p", object);
231 return prim_id.AppendElementString(name);
232}
233
234pxr::SdfPath InstancerData::hair_prim_id(Object *parent_obj, const ParticleSystem *psys) const
235{
236 /* Making id of object in form like <prefix>_<pointer in 16 hex digits format> */
237 char name[128];
238 SNPRINTF(name, "%s_PS_%p", object_prim_id(parent_obj).GetName().c_str(), psys);
239 return prim_id.AppendElementString(name);
240}
241
242pxr::SdfPath InstancerData::nonmesh_prim_id(pxr::SdfPath const &prim_id, int index) const
243{
244 char name[16];
245 SNPRINTF(name, "NM_%08d", index);
246 return prim_id.AppendElementString(name);
247}
248
249int InstancerData::nonmesh_prim_id_index(pxr::SdfPath const &id) const
250{
251 int index;
252 sscanf(id.GetName().c_str(), "NM_%d", &index);
253 return index;
254}
255
256void InstancerData::update_nonmesh_instance(NonmeshInstance &nm_inst)
257{
258 ObjectData *obj_data = nm_inst.data.get();
259 pxr::SdfPath prev_id = nm_inst.data->prim_id;
260 int i;
261
262 /* Remove old Nonmesh instances */
263 while (nm_inst.count > nm_inst.transforms.size()) {
264 --nm_inst.count;
265 obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count);
266 obj_data->remove();
267 }
268
269 /* NOTE: Special case: recreate instances when prim_type was changed.
270 * Doing only update for other Nonmesh objects. */
271 LightData *l_data = dynamic_cast<LightData *>(obj_data);
272 if (l_data && l_data->prim_type((Light *)((Object *)l_data->id)->data) != l_data->prim_type_) {
273 for (i = 0; i < nm_inst.count; ++i) {
274 obj_data->prim_id = nonmesh_prim_id(prev_id, i);
275 obj_data->remove();
276 }
277 l_data->init();
278 for (i = 0; i < nm_inst.count; ++i) {
279 obj_data->prim_id = nonmesh_prim_id(prev_id, i);
280 obj_data->insert();
281 }
282 }
283 else {
284 for (i = 0; i < nm_inst.count; ++i) {
285 obj_data->prim_id = nonmesh_prim_id(prev_id, i);
286 obj_data->update();
287 }
288 }
289
290 /* Add new Nonmesh instances */
291 while (nm_inst.count < nm_inst.transforms.size()) {
292 obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count);
293 obj_data->insert();
294 ++nm_inst.count;
295 }
296
297 obj_data->prim_id = prev_id;
298}
299
300InstancerData::MeshInstance *InstancerData::mesh_instance(pxr::SdfPath const &id) const
301{
302 auto m_inst = mesh_instances_.lookup_ptr(id.GetPathElementCount() == 4 ? id.GetParentPath() :
303 id);
304 if (!m_inst) {
305 return nullptr;
306 }
307 return const_cast<MeshInstance *>(m_inst);
308}
309
310InstancerData::NonmeshInstance *InstancerData::nonmesh_instance(pxr::SdfPath const &id) const
311{
312 auto nm_inst = nonmesh_instances_.lookup_ptr(id.GetPathElementCount() == 4 ? id.GetParentPath() :
313 id);
314 if (!nm_inst) {
315 return nullptr;
316 }
317 return const_cast<NonmeshInstance *>(nm_inst);
318}
319
320} // namespace blender::io::hydra
bool psys_in_edit_mode(struct Depsgraph *depsgraph, const struct ParticleSystem *psys)
#define LISTBASE_FOREACH(type, var, list)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
void clear()
Definition BLI_map.hh:989
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:601
ValueIterator values() const
Definition BLI_map.hh:846
bool is_empty() const
Definition BLI_map.hh:937
int64_t remove_if(Predicate &&predicate)
Definition BLI_map.hh:899
static bool is_supported(const ParticleSystem *particle_system)
static bool is_visible(HydraSceneDelegate *scene_delegate, Object *object, ParticleSystem *particle_system)
pxr::SdfPath prim_id
Definition id.hh:36
HydraSceneDelegate * scene_delegate_
Definition id.hh:39
pxr::GfMatrix4d transform(pxr::SdfPath const &id) const
Definition instancer.cc:61
pxr::VtIntArray indices(pxr::SdfPath const &id) const
Definition instancer.cc:83
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const
Definition instancer.cc:72
void update_instance(DupliObject *dupli)
Definition instancer.cc:140
InstancerData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id)
Definition instancer.cc:23
pxr::VtValue get_data(pxr::TfToken const &key) const override
Definition instancer.cc:52
void available_materials(Set< pxr::SdfPath > &paths) const
Definition instancer.cc:112
pxr::SdfPathVector prototypes() const
Definition instancer.cc:101
ObjectData * object_data(pxr::SdfPath const &id) const
Definition instancer.cc:88
void update_double_sided(MaterialData *mat_data)
Definition instancer.cc:122
static bool is_mesh(const Object *object)
static std::unique_ptr< ObjectData > create(HydraSceneDelegate *scene_delegate, const Object *object, pxr::SdfPath const &prim_id)
#define ID_LOG(level, msg,...)
Definition id.hh:53
struct CLG_LogRef * LOG_HYDRA_SCENE
pxr::GfMatrix4d gf_matrix_from_transform(const float m[4][4])
float mat[4][4]