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