Blender V5.0
scene/object.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "scene/object.h"
6
7#include "device/device.h"
8#include "scene/camera.h"
9#include "scene/curves.h"
10#include "scene/hair.h"
11#include "scene/integrator.h"
12#include "scene/light.h"
13#include "scene/mesh.h"
14#include "scene/particles.h"
15#include "scene/pointcloud.h"
16#include "scene/scene.h"
17#include "scene/stats.h"
18#include "scene/volume.h"
19
20#include "util/log.h"
21#include "util/map.h"
22#include "util/murmurhash.h"
23#include "util/progress.h"
24#include "util/set.h"
25#include "util/tbb.h"
26#include "util/vector.h"
27
29
30/* Global state of object transform update. */
31
33 /* Global state used by device_update_object_transform().
34 * Common for both threaded and non-threaded update.
35 */
36
37 /* Type of the motion required by the scene settings. */
39
40 /* Mapping from particle system to a index in packed particle array.
41 * Only used for read.
42 */
43 map<ParticleSystem *, int> particle_offset;
44
45 /* Motion offsets for each object. */
47
48 /* Packed object arrays. Those will be filled in. */
54
55 /* Flags which will be synchronized to Integrator. */
60
61 /* ** Scheduling queue. ** */
63
64 /* First unused object index in the queue. */
66};
67
68/* Object */
69
71{
72 NodeType *type = NodeType::add("object", create);
73
74 SOCKET_NODE(geometry, "Geometry", Geometry::get_node_base_type());
75 SOCKET_TRANSFORM(tfm, "Transform", transform_identity());
76 SOCKET_UINT(visibility, "Visibility", ~0);
77 SOCKET_COLOR(color, "Color", zero_float3());
78 SOCKET_FLOAT(alpha, "Alpha", 0.0f);
79 SOCKET_UINT(random_id, "Random ID", 0);
80 SOCKET_INT(pass_id, "Pass ID", 0);
81 SOCKET_BOOLEAN(use_holdout, "Use Holdout", false);
82 SOCKET_BOOLEAN(hide_on_missing_motion, "Hide on Missing Motion", false);
83 SOCKET_POINT(dupli_generated, "Dupli Generated", zero_float3());
84 SOCKET_POINT2(dupli_uv, "Dupli UV", zero_float2());
85 SOCKET_TRANSFORM_ARRAY(motion, "Motion", array<Transform>());
86 SOCKET_FLOAT(shadow_terminator_shading_offset, "Shadow Terminator Shading Offset", 0.0f);
87 SOCKET_FLOAT(shadow_terminator_geometry_offset, "Shadow Terminator Geometry Offset", 0.1f);
88 SOCKET_STRING(asset_name, "Asset Name", ustring());
89
90 SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", false);
91
92 SOCKET_BOOLEAN(is_caustics_caster, "Cast Shadow Caustics", false);
93 SOCKET_BOOLEAN(is_caustics_receiver, "Receive Shadow Caustics", false);
94
95 SOCKET_BOOLEAN(is_bake_target, "Bake Target", false);
96
97 SOCKET_NODE(particle_system, "Particle System", ParticleSystem::get_node_type());
98 SOCKET_INT(particle_index, "Particle Index", 0);
99
100 SOCKET_FLOAT(ao_distance, "AO Distance", 0.0f);
101
102 SOCKET_STRING(lightgroup, "Light Group", ustring());
103 SOCKET_UINT(receiver_light_set, "Light Set Index", 0);
104 SOCKET_UINT64(light_set_membership, "Light Set Membership", LIGHT_LINK_MASK_ALL);
105 SOCKET_UINT(blocker_shadow_set, "Shadow Set Index", 0);
106 SOCKET_UINT64(shadow_set_membership, "Shadow Set Membership", LIGHT_LINK_MASK_ALL);
107
108 return type;
109}
110
111Object::Object() : Node(get_node_type())
112{
113 particle_system = nullptr;
114 particle_index = 0;
115 attr_map_offset = 0;
117 intersects_volume = false;
118}
119
120Object::~Object() = default;
121
123{
124 if (!use_motion()) {
125 return;
126 }
127
128 bool have_motion = false;
129
130 for (size_t i = 0; i < motion.size(); i++) {
131 if (motion[i] == transform_empty()) {
132 if (hide_on_missing_motion) {
133 /* Hide objects that have no valid previous or next
134 * transform, for example particle that stop existing. It
135 * would be better to handle this in the kernel and make
136 * objects invisible outside certain motion steps. */
137 tfm = transform_empty();
138 motion.clear();
139 return;
140 }
141 /* Otherwise just copy center motion. */
142 motion[i] = tfm;
143 }
144
145 /* Test if any of the transforms are actually different. */
146 have_motion = have_motion || motion[i] != tfm;
147 }
148
149 /* Clear motion array if there is no actual motion. */
150 if (!have_motion) {
151 motion.clear();
152 }
153}
154
155void Object::compute_bounds(bool motion_blur)
156{
157 const BoundBox mbounds = geometry->bounds;
158
159 if (motion_blur && use_motion()) {
160 array<DecomposedTransform> decomp(motion.size());
161 transform_motion_decompose(decomp.data(), motion.data(), motion.size());
162
164
165 /* TODO: this is really terrible. according to PBRT there is a better
166 * way to find this iteratively, but did not find implementation yet
167 * or try to implement myself */
168 for (float t = 0.0f; t < 1.0f; t += (1.0f / 128.0f)) {
169 Transform ttfm;
170
171 transform_motion_array_interpolate(&ttfm, decomp.data(), motion.size(), t);
172 bounds.grow(mbounds.transformed(&ttfm));
173 }
174 }
175 else {
176 /* No motion blur case. */
177 if (geometry->transform_applied) {
178 bounds = mbounds;
179 }
180 else {
181 bounds = mbounds.transformed(&tfm);
182 }
183 }
184}
185
186void Object::apply_transform(bool apply_to_motion)
187{
188 if (!geometry || tfm == transform_identity()) {
189 return;
190 }
191
192 geometry->apply_transform(tfm, apply_to_motion);
193
194 /* we keep normals pointing in same direction on negative scale, notify
195 * geometry about this in it (re)calculates normals */
196 if (transform_negative_scale(tfm)) {
197 geometry->transform_negative_scaled = true;
198 }
199
200 if (bounds.valid()) {
201 geometry->compute_bounds();
202 compute_bounds(false);
203 }
204
205 /* tfm is not reset to identity, all code that uses it needs to check the
206 * transform_applied boolean */
207}
208
210{
212
213 if (is_modified()) {
215
216 if (use_holdout_is_modified()) {
218 }
219
220 if (is_shadow_catcher_is_modified()) {
223 }
224 }
225
226 if (geometry) {
227 if (tfm_is_modified() || motion_is_modified()) {
229 if (geometry->has_volume) {
230 scene->volume_manager->tag_update(this, flag);
231 }
232 }
233
234 if (visibility_is_modified()) {
236 }
237
238 for (Node *node : geometry->get_used_shaders()) {
239 Shader *shader = static_cast<Shader *>(node);
241 scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
242 }
243 }
244 }
245
246 scene->camera->need_flags_update = true;
247 scene->object_manager->tag_update(scene, flag);
248}
249
251{
252 return (motion.size() > 1);
253}
254
255float Object::motion_time(const int step) const
256{
257 return (use_motion()) ? 2.0f * step / (motion.size() - 1) - 1.0f : 0.0f;
258}
259
260int Object::motion_step(const float time) const
261{
262 if (use_motion()) {
263 for (size_t step = 0; step < motion.size(); step++) {
264 if (time == motion_time(step)) {
265 return step;
266 }
267 }
268 }
269
270 return -1;
271}
272
274{
275 /* Not supported for lights yet. */
276 if (geometry->is_light()) {
277 return false;
278 }
279 /* Mesh itself can be empty,can skip all such objects. */
280 if (!bounds.valid() || bounds.size() == zero_float3()) {
281 return false;
282 }
283 /* TODO(sergey): Check for mesh vertices/curves. visibility flags. */
284 return true;
285}
286
288{
289 return SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility & PATH_RAY_ALL_VISIBILITY);
290}
291
293{
294 if (geometry->is_light()) {
295 /* World volume. */
296 assert(static_cast<const Light *>(geometry)->get_light_type() == LIGHT_BACKGROUND);
297 for (const Node *node : geometry->get_used_shaders()) {
298 const Shader *shader = static_cast<const Shader *>(node);
299 if (shader->has_volume) {
300 return shader->get_volume_step_rate();
301 }
302 }
303 assert(false);
304 return FLT_MAX;
305 }
306
307 if (!geometry->is_mesh() && !geometry->is_volume()) {
308 return FLT_MAX;
309 }
310
311 Mesh *mesh = static_cast<Mesh *>(geometry);
312
313 if (!mesh->has_volume) {
314 return FLT_MAX;
315 }
316
317 /* Compute step rate from shaders. */
318 float step_rate = FLT_MAX;
319
320 for (Node *node : mesh->get_used_shaders()) {
321 Shader *shader = static_cast<Shader *>(node);
322 if (shader->has_volume) {
324 step_rate = fminf(shader->get_volume_step_rate(), step_rate);
325 }
326 }
327 }
328
329 if (step_rate == FLT_MAX) {
330 return FLT_MAX;
331 }
332
333 /* Compute step size from voxel grids. */
334 float step_size = FLT_MAX;
335
336 if (geometry->is_volume()) {
337 Volume *volume = static_cast<Volume *>(geometry);
338
339 for (Attribute &attr : volume->attributes.attributes) {
340 if (attr.element == ATTR_ELEMENT_VOXEL) {
341 ImageHandle &handle = attr.data_voxel();
342 const ImageMetaData &metadata = handle.metadata();
343 if (metadata.byte_size == 0) {
344 continue;
345 }
346
347 /* User specified step size. */
348 float voxel_step_size = volume->get_step_size();
349
350 if (voxel_step_size == 0.0f) {
351 /* Auto detect step size.
352 * Step size is transformed from voxel to world space. */
353 Transform voxel_tfm = tfm;
354 if (metadata.use_transform_3d) {
355 voxel_tfm = tfm * transform_inverse(metadata.transform_3d);
356 }
357 voxel_step_size = reduce_min(fabs(transform_direction(&voxel_tfm, one_float3())));
358 }
359 else if (volume->get_object_space()) {
360 /* User specified step size in object space. */
361 const float3 size = make_float3(voxel_step_size, voxel_step_size, voxel_step_size);
362 voxel_step_size = reduce_min(fabs(transform_direction(&tfm, size)));
363 }
364
365 if (voxel_step_size > 0.0f) {
366 step_size = fminf(voxel_step_size, step_size);
367 }
368 }
369 }
370 }
371
372 if (step_size == FLT_MAX) {
373 /* Fall back to 1/10th of bounds for procedural volumes. */
374 assert(bounds.valid());
375 step_size = 0.1f * average(bounds.size());
376 }
377
378 step_size *= step_rate;
379
380 return step_size;
381}
382
384{
385 return index;
386}
387
389{
390 Geometry *geom = get_geometry();
391 if (!geom->is_mesh() && !geom->is_volume()) {
392 return false;
393 }
394 /* Skip non-traceable objects. */
395 if (!is_traceable()) {
396 return false;
397 }
398 /* Skip if we are not visible for BSDFs. */
399 if (!(get_visibility() &
401 {
402 return false;
403 }
404 /* Skip if we have no emission shaders. */
405 /* TODO(sergey): Ideally we want to avoid such duplicated loop, since it'll
406 * iterate all geometry shaders twice (when counting and when calculating
407 * triangle area.
408 */
409 for (Node *node : geom->get_used_shaders()) {
410 Shader *shader = static_cast<Shader *>(node);
412 return true;
413 }
414 }
415 return false;
416}
417
419{
420 if (get_receiver_light_set()) {
421 return true;
422 }
423
424 if (get_light_set_membership() != LIGHT_LINK_MASK_ALL) {
425 return true;
426 }
427
428 return false;
429}
430
432{
433 if (get_blocker_shadow_set()) {
434 return true;
435 }
436
437 if (get_shadow_set_membership() != LIGHT_LINK_MASK_ALL) {
438 return true;
439 }
440
441 return false;
442}
443
444/* Object Manager */
445
447{
448 update_flags = UPDATE_ALL;
449 need_flags_update = true;
450}
451
453
454static float object_volume_density(const Transform &tfm, Geometry *geom)
455{
456 if (geom->is_volume()) {
457 /* Volume density automatically adjust to object scale. */
458 if (static_cast<Volume *>(geom)->get_object_space()) {
459 const float3 unit = normalize(one_float3());
460 return 1.0f / len(transform_direction(&tfm, unit));
461 }
462 }
463
464 return 1.0f;
465}
466
468{
469 return (geom->is_mesh() || geom->is_volume()) ? static_cast<Mesh *>(geom)->get_verts().size() :
470 geom->is_hair() ? static_cast<Hair *>(geom)->get_curve_keys().size() :
471 geom->is_pointcloud() ? static_cast<PointCloud *>(geom)->num_points() :
472 0;
473}
474
476 Object *ob,
477 bool update_all,
478 const Scene *scene)
479{
480 KernelObject &kobject = state->objects[ob->index];
481 Transform *object_motion_pass = state->object_motion_pass;
482
483 Geometry *geom = ob->geometry;
484 uint flag = 0;
485
486 /* Compute transformations. */
487 const Transform tfm = ob->tfm;
488 const Transform itfm = transform_inverse(tfm);
489
490 const float3 color = ob->color;
491 const float pass_id = ob->pass_id;
492 const float random_number = (float)ob->random_id * (1.0f / (float)0xFFFFFFFF);
493 const int particle_index = (ob->particle_system) ?
494 ob->particle_index + state->particle_offset[ob->particle_system] :
495 0;
496
497 kobject.tfm = tfm;
498 kobject.itfm = itfm;
499 kobject.volume_density = object_volume_density(tfm, geom);
500 kobject.color[0] = color.x;
501 kobject.color[1] = color.y;
502 kobject.color[2] = color.z;
503 kobject.alpha = ob->alpha;
504 kobject.pass_id = pass_id;
505 kobject.random_number = random_number;
507 kobject.motion_offset = 0;
508 kobject.ao_distance = ob->ao_distance;
509 kobject.receiver_light_set = ob->receiver_light_set >= LIGHT_LINK_SET_MAX ?
510 0 :
511 ob->receiver_light_set;
512 kobject.light_set_membership = ob->light_set_membership;
513 kobject.blocker_shadow_set = ob->blocker_shadow_set >= LIGHT_LINK_SET_MAX ?
514 0 :
515 ob->blocker_shadow_set;
516 kobject.shadow_set_membership = ob->shadow_set_membership;
517
518 if (geom->get_use_motion_blur()) {
519 state->have_motion = true;
520 }
521
522 if (transform_negative_scale(tfm)) {
524 }
525
526 /* TODO: why not check hair? */
527 if (geom->is_pointcloud()) {
530 }
531 }
532 else if (geom->is_mesh()) {
533 Mesh *mesh = static_cast<Mesh *>(geom);
535 (mesh->get_subdivision_type() != Mesh::SUBDIVISION_NONE &&
537 {
539 }
540 }
541 else if (geom->is_volume()) {
542 Volume *volume = static_cast<Volume *>(geom);
543 if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY) && volume->get_velocity_scale() != 0.0f)
544 {
546 kobject.velocity_scale = volume->get_velocity_scale();
547 }
548 }
549
550 if (state->need_motion == Scene::MOTION_PASS) {
551 /* Clear motion array if there is no actual motion. */
552 ob->update_motion();
553
554 /* Compute motion transforms. */
555 Transform tfm_pre;
556 Transform tfm_post;
557 if (ob->use_motion()) {
558 tfm_pre = ob->motion[0];
559 tfm_post = ob->motion[ob->motion.size() - 1];
560 }
561 else {
562 tfm_pre = tfm;
563 tfm_post = tfm;
564 }
565
566 /* Motion transformations, is world/object space depending if mesh
567 * comes with deformed position in object space, or if we transform
568 * the shading point in world space. */
570 tfm_pre = tfm_pre * itfm;
571 tfm_post = tfm_post * itfm;
572 }
573
574 const int motion_pass_offset = ob->index * OBJECT_MOTION_PASS_SIZE;
575 object_motion_pass[motion_pass_offset + 0] = tfm_pre;
576 object_motion_pass[motion_pass_offset + 1] = tfm_post;
577 }
578 else if (state->need_motion == Scene::MOTION_BLUR) {
579 if (ob->use_motion()) {
580 kobject.motion_offset = state->motion_offset[ob->index];
581
582 /* Decompose transforms for interpolation. */
583 if (ob->tfm_is_modified() || ob->motion_is_modified() || update_all) {
584 DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
585 transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
586 }
587
589 state->have_motion = true;
590 }
591 }
592
593 /* Dupli object coords and motion info. */
594 kobject.dupli_generated[0] = ob->dupli_generated[0];
595 kobject.dupli_generated[1] = ob->dupli_generated[1];
596 kobject.dupli_generated[2] = ob->dupli_generated[2];
597 kobject.dupli_uv[0] = ob->dupli_uv[0];
598 kobject.dupli_uv[1] = ob->dupli_uv[1];
599 kobject.num_geom_steps = (geom->get_motion_steps() - 1) / 2;
600 kobject.num_tfm_steps = ob->motion.size();
601 kobject.numverts = object_num_motion_verts(geom);
602 kobject.attribute_map_offset = 0;
603
604 if (ob->asset_name_is_modified() || update_all) {
605 const uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
606 const uint32_t hash_asset = util_murmur_hash3(
607 ob->asset_name.c_str(), ob->asset_name.length(), 0);
608 kobject.cryptomatte_object = util_hash_to_float(hash_name);
609 kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
610 }
611
612 kobject.shadow_terminator_shading_offset = 1.0f /
613 (1.0f - 0.5f * ob->shadow_terminator_shading_offset);
615
616 kobject.visibility = ob->visibility_for_tracing();
617 kobject.primitive_type = geom->primitive_type();
618
619 /* Object shadow caustics flag */
620 if (ob->is_caustics_caster) {
622 }
623 if (ob->is_caustics_receiver) {
625 }
626
627 /* Object flag. */
628 if (ob->use_holdout) {
630 }
631 state->object_flag[ob->index] = flag;
632
633 /* Have curves. */
634 if (geom->is_hair()) {
635 state->have_curves = true;
636 }
637 if (geom->is_pointcloud()) {
638 state->have_points = true;
639 }
640 if (geom->is_volume()) {
641 state->have_volumes = true;
642 }
643
644 /* Light group. */
645 auto it = scene->lightgroups.find(ob->lightgroup);
646 if (it != scene->lightgroups.end()) {
647 kobject.lightgroup = it->second;
648 }
649 else {
650 kobject.lightgroup = LIGHTGROUP_NONE;
651 }
652}
653
655{
656 if (!scene->integrator->get_use_light_tree()) {
657 const BVHLayoutMask layout_mask = device->get_bvh_layout_mask(dscene->data.kernel_features);
658 if (layout_mask != BVH_LAYOUT_METAL && layout_mask != BVH_LAYOUT_MULTI_METAL &&
659 layout_mask != BVH_LAYOUT_MULTI_METAL_EMBREE && layout_mask != BVH_LAYOUT_HIPRT &&
660 layout_mask != BVH_LAYOUT_MULTI_HIPRT && layout_mask != BVH_LAYOUT_MULTI_HIPRT_EMBREE)
661 {
662 return;
663 }
664 }
665
666 /* On MetalRT, primitive / curve segment offsets can't be baked at BVH build time. Intersection
667 * handlers need to apply the offset manually. */
668 uint *object_prim_offset = dscene->object_prim_offset.alloc(scene->objects.size());
669 for (Object *ob : scene->objects) {
670 uint32_t prim_offset = 0;
671 if (Geometry *const geom = ob->geometry) {
672 if (geom->is_hair()) {
673 prim_offset = ((Hair *const)geom)->curve_segment_offset;
674 }
675 else {
676 prim_offset = geom->prim_offset;
677 }
678 }
679 const uint obj_index = ob->get_device_index();
680 object_prim_offset[obj_index] = prim_offset;
681 }
682
685}
686
688{
690 state.need_motion = scene->need_motion();
691 state.have_motion = false;
692 state.have_curves = false;
693 state.have_points = false;
694 state.have_volumes = false;
695 state.scene = scene;
696 state.queue_start_object = 0;
697
698 state.objects = dscene->objects.alloc(scene->objects.size());
699 state.object_flag = dscene->object_flag.alloc(scene->objects.size());
700 state.object_motion = nullptr;
701 state.object_motion_pass = nullptr;
702
703 if (state.need_motion == Scene::MOTION_PASS) {
704 state.object_motion_pass = dscene->object_motion_pass.alloc(OBJECT_MOTION_PASS_SIZE *
705 scene->objects.size());
706 }
707 else if (state.need_motion == Scene::MOTION_BLUR) {
708 /* Set object offsets into global object motion array. */
709 uint *motion_offsets = state.motion_offset.resize(scene->objects.size());
710 uint motion_offset = 0;
711
712 for (Object *ob : scene->objects) {
713 *motion_offsets = motion_offset;
714 motion_offsets++;
715
716 /* Clear motion array if there is no actual motion. */
717 ob->update_motion();
718 motion_offset += ob->motion.size();
719 }
720
721 state.object_motion = dscene->object_motion.alloc(motion_offset);
722 }
723
724 /* Particle system device offsets
725 * 0 is dummy particle, index starts at 1.
726 */
727 int numparticles = 1;
728 for (ParticleSystem *psys : scene->particle_systems) {
729 state.particle_offset[psys] = numparticles;
730 numparticles += psys->particles.size();
731 }
732
733 /* as all the arrays are the same size, checking only dscene.objects is sufficient */
734 const bool update_all = dscene->objects.need_realloc();
735
736 /* Parallel object update, with grain size to avoid too much threading overhead
737 * for individual objects. */
738 static const int OBJECTS_PER_TASK = 32;
739 parallel_for(blocked_range<size_t>(0, scene->objects.size(), OBJECTS_PER_TASK),
740 [&](const blocked_range<size_t> &r) {
741 for (size_t i = r.begin(); i != r.end(); i++) {
742 Object *ob = state.scene->objects[i];
743 device_update_object_transform(&state, ob, update_all, scene);
744 }
745 });
746
747 if (progress.get_cancel()) {
748 return;
749 }
750
751 dscene->objects.copy_to_device_if_modified();
752 if (state.need_motion == Scene::MOTION_PASS) {
753 dscene->object_motion_pass.copy_to_device();
754 }
755 else if (state.need_motion == Scene::MOTION_BLUR) {
756 dscene->object_motion.copy_to_device();
757 }
758
759 dscene->data.bvh.have_motion = state.have_motion;
760 dscene->data.bvh.have_curves = state.have_curves;
761 dscene->data.bvh.have_points = state.have_points;
762 dscene->data.bvh.have_volumes = state.have_volumes;
763
764 dscene->objects.clear_modified();
765 dscene->object_motion_pass.clear_modified();
766 dscene->object_motion.clear_modified();
767}
768
770 DeviceScene *dscene,
771 Scene *scene,
772 Progress &progress)
773{
774 if (!need_update()) {
775 return;
776 }
777
778 if (update_flags & (OBJECT_ADDED | OBJECT_REMOVED)) {
779 dscene->objects.tag_realloc();
781 dscene->object_motion.tag_realloc();
782 dscene->object_flag.tag_realloc();
784
785 /* If objects are added to the scene or deleted, the object indices might change, so we need to
786 * update the root indices of the volume octrees. */
787 scene->volume_manager->tag_update_indices();
788 }
789
790 if (update_flags & HOLDOUT_MODIFIED) {
791 dscene->object_flag.tag_modified();
792 }
793
794 if (update_flags & PARTICLE_MODIFIED) {
795 dscene->objects.tag_modified();
796 }
797
798 LOG_INFO << "Total " << scene->objects.size() << " objects.";
799
800 device_free(device, dscene, false);
801
802 if (scene->objects.empty()) {
803 return;
804 }
805
806 {
807 /* Assign object IDs. */
808 const scoped_callback_timer timer([scene](double time) {
809 if (scene->update_stats) {
810 scene->update_stats->object.times.add_entry({"device_update (assign index)", time});
811 }
812 });
813
814 int index = 0;
815 for (Object *object : scene->objects) {
816 object->index = index++;
817
818 /* this is a bit too broad, however a bigger refactor might be needed to properly separate
819 * update each type of data (transform, flags, etc.) */
820 if (object->is_modified()) {
821 dscene->objects.tag_modified();
823 dscene->object_motion.tag_modified();
824 dscene->object_flag.tag_modified();
826 }
827
828 /* Update world object index. */
829 if (!object->get_geometry()->is_light()) {
830 continue;
831 }
832
833 const Light *light = static_cast<const Light *>(object->get_geometry());
834 if (light->get_light_type() == LIGHT_BACKGROUND) {
835 dscene->data.background.object_index = object->index;
836 }
837 }
838 }
839
840 {
841 /* set object transform matrices, before applying static transforms */
842 const scoped_callback_timer timer([scene](double time) {
843 if (scene->update_stats) {
844 scene->update_stats->object.times.add_entry(
845 {"device_update (copy objects to device)", time});
846 }
847 });
848
849 progress.set_status("Updating Objects", "Copying Transformations to device");
850 device_update_transforms(dscene, scene, progress);
851 }
852
853 for (Object *object : scene->objects) {
854 object->clear_modified();
855 }
856}
857
859 DeviceScene *dscene,
860 Scene *scene,
861 Progress & /*progress*/,
862 bool bounds_valid)
863{
864 if (!need_update() && !need_flags_update) {
865 return;
866 }
867
868 const scoped_callback_timer timer([scene](double time) {
869 if (scene->update_stats) {
870 scene->update_stats->object.times.add_entry({"device_update_flags", time});
871 }
872 });
873
874 if (bounds_valid) {
875 /* Object flags and calculations related to volume depend on proper bounds calculated, which
876 * might not be available yet when object flags are updated for displacement or hair
877 * transparency calculation. In this case do not clear the need_flags_update, so that these
878 * values which depend on bounds are re-calculated when the device_update process comes back
879 * here from the "Updating Objects Flags" stage. */
880 update_flags = UPDATE_NONE;
881 need_flags_update = false;
882 }
883
884 if (scene->objects.empty()) {
885 return;
886 }
887
888 /* Object info flag. */
889 uint *object_flag = dscene->object_flag.data();
890
891 /* Object volume intersection. */
892 vector<Object *> volume_objects;
893 bool has_volume_objects = false;
894 for (Object *object : scene->objects) {
895 if (object->geometry->has_volume) {
896 /* If the bounds are not valid it is not always possible to calculate the volume step, and
897 * the step size is not needed for the displacement. So, delay calculation of the volume
898 * step size until the final bounds are known. */
899 if (bounds_valid) {
900 volume_objects.push_back(object);
901 }
902 has_volume_objects = true;
903 }
904 }
905
906 for (Object *object : scene->objects) {
907 if (object->geometry->has_volume) {
908 object_flag[object->index] |= SD_OBJECT_HAS_VOLUME;
909 object_flag[object->index] &= ~SD_OBJECT_HAS_VOLUME_ATTRIBUTES;
910
911 for (const Attribute &attr : object->geometry->attributes.attributes) {
912 if (attr.element == ATTR_ELEMENT_VOXEL) {
913 object_flag[object->index] |= SD_OBJECT_HAS_VOLUME_ATTRIBUTES;
914 }
915 }
916 }
917 else {
918 object_flag[object->index] &= ~(SD_OBJECT_HAS_VOLUME | SD_OBJECT_HAS_VOLUME_ATTRIBUTES);
919 }
920
921 if (object->is_shadow_catcher) {
922 object_flag[object->index] |= SD_OBJECT_SHADOW_CATCHER;
923 }
924 else {
925 object_flag[object->index] &= ~SD_OBJECT_SHADOW_CATCHER;
926 }
927
928 if (bounds_valid) {
929 object->intersects_volume = false;
930 for (Object *volume_object : volume_objects) {
931 if (object == volume_object) {
932 continue;
933 }
934 if (object->bounds.intersects(volume_object->bounds)) {
935 object_flag[object->index] |= SD_OBJECT_INTERSECTS_VOLUME;
936 object->intersects_volume = true;
937 break;
938 }
939 }
940 }
941 else if (has_volume_objects) {
942 /* Not really valid, but can't make more reliable in the case
943 * of bounds not being up to date.
944 */
945 object_flag[object->index] |= SD_OBJECT_INTERSECTS_VOLUME;
946 }
947 }
948
949 /* Copy object flag. */
950 dscene->object_flag.copy_to_device();
951 dscene->object_flag.clear_modified();
952}
953
955 DeviceScene *dscene,
956 Scene *scene)
957{
958 if (dscene->objects.size() == 0) {
959 return;
960 }
961
962 KernelObject *kobjects = dscene->objects.data();
963
964 bool update = false;
965
966 for (Object *object : scene->objects) {
967 Geometry *geom = object->geometry;
968
969 size_t attr_map_offset = object->attr_map_offset;
970
971 /* An object attribute map cannot have a zero offset because mesh maps come first. */
972 if (attr_map_offset == 0) {
973 attr_map_offset = geom->attr_map_offset;
974 }
975
976 KernelObject &kobject = kobjects[object->index];
977
978 if (kobject.attribute_map_offset != attr_map_offset) {
979 kobject.attribute_map_offset = attr_map_offset;
980 update = true;
981 }
982
983 const int numverts = object_num_motion_verts(geom);
984 if (kobject.numverts != numverts) {
985 kobject.numverts = numverts;
986 update = true;
987 }
988 }
989
990 if (update) {
991 dscene->objects.copy_to_device();
992 }
993}
994
995void ObjectManager::device_free(Device * /*unused*/, DeviceScene *dscene, bool force_free)
996{
997 dscene->objects.free_if_need_realloc(force_free);
998 dscene->object_motion_pass.free_if_need_realloc(force_free);
999 dscene->object_motion.free_if_need_realloc(force_free);
1000 dscene->object_flag.free_if_need_realloc(force_free);
1001 dscene->object_prim_offset.free_if_need_realloc(force_free);
1002}
1003
1005{
1006 /* todo: normals and displacement should be done before applying transform! */
1007 /* todo: create objects/geometry in right order! */
1008
1009 /* counter geometry users */
1010 map<Geometry *, int> geometry_users;
1011 const Scene::MotionType need_motion = scene->need_motion();
1012 const bool motion_blur = need_motion == Scene::MOTION_BLUR;
1013 const bool apply_to_motion = need_motion != Scene::MOTION_PASS;
1014 int i = 0;
1015
1016 for (Object *object : scene->objects) {
1017 const map<Geometry *, int>::iterator it = geometry_users.find(object->geometry);
1018
1019 if (it == geometry_users.end()) {
1020 geometry_users[object->geometry] = 1;
1021 }
1022 else {
1023 it->second++;
1024 }
1025 }
1026
1027 if (progress.get_cancel()) {
1028 return;
1029 }
1030
1031 uint *object_flag = dscene->object_flag.data();
1032
1033 /* apply transforms for objects with single user geometry */
1034 for (Object *object : scene->objects) {
1035 /* Annoying feedback loop here: we can't use is_instanced() because
1036 * it'll use uninitialized transform_applied flag.
1037 *
1038 * Could be solved by moving reference counter to Geometry.
1039 */
1040 Geometry *geom = object->geometry;
1041 bool apply = (geometry_users[geom] == 1) && !geom->has_surface_bssrdf &&
1042 !geom->has_true_displacement();
1043
1044 if (geom->is_mesh()) {
1045 Mesh *mesh = static_cast<Mesh *>(geom);
1046 apply = apply && mesh->get_subdivision_type() == Mesh::SUBDIVISION_NONE;
1047 }
1048 else if (geom->is_hair() || geom->is_pointcloud()) {
1049 /* Can't apply non-uniform scale to curves and points, this can't be
1050 * represented by control points and radius alone. */
1051 float scale;
1052 apply = apply && transform_uniform_scale(object->tfm, scale);
1053 }
1054
1055 if (apply) {
1056 if (!(motion_blur && object->use_motion())) {
1057 if (!geom->transform_applied) {
1058 object->apply_transform(apply_to_motion);
1059 geom->transform_applied = true;
1060
1061 if (progress.get_cancel()) {
1062 return;
1063 }
1064 }
1065
1066 object_flag[i] |= SD_OBJECT_TRANSFORM_APPLIED;
1067 }
1068 }
1069
1070 i++;
1071 }
1072}
1073
1074void ObjectManager::tag_update(Scene *scene, const uint32_t flag)
1075{
1076 update_flags |= flag;
1077
1078 /* avoid infinite loops if the geometry manager tagged us for an update */
1079 if ((flag & GEOMETRY_MANAGER) == 0) {
1080 uint32_t geometry_flag = GeometryManager::OBJECT_MANAGER;
1081
1082 /* Also notify in case added or removed objects were instances, as no Geometry might have been
1083 * added or removed, but the BVH still needs to updated. */
1084 if ((flag & (OBJECT_ADDED | OBJECT_REMOVED)) != 0) {
1086 }
1087
1088 if ((flag & TRANSFORM_MODIFIED) != 0) {
1089 geometry_flag |= GeometryManager::TRANSFORM_MODIFIED;
1090 }
1091
1092 if ((flag & VISIBILITY_MODIFIED) != 0) {
1093 geometry_flag |= GeometryManager::VISIBILITY_MODIFIED;
1094 }
1095
1096 scene->geometry_manager->tag_update(scene, geometry_flag);
1097 }
1098
1099 scene->light_manager->tag_update(scene, LightManager::OBJECT_MANAGER);
1100
1101 /* Integrator's shadow catcher settings depends on object visibility settings. */
1104 }
1105}
1106
1108{
1109 return update_flags != UPDATE_NONE;
1110}
1111
1113{
1114 string manifest = "{";
1115
1116 unordered_set<ustring> objects;
1117 for (Object *object : scene->objects) {
1118 if (objects.count(object->name)) {
1119 continue;
1120 }
1121 objects.insert(object->name);
1122 const uint32_t hash_name = util_murmur_hash3(object->name.c_str(), object->name.length(), 0);
1123 manifest += string_printf("\"%s\":\"%08x\",", object->name.c_str(), hash_name);
1124 }
1125 manifest[manifest.size() - 1] = '}';
1126 return manifest;
1127}
1128
1130{
1131 string manifest = "{";
1132 unordered_set<ustring> assets;
1133 for (Object *ob : scene->objects) {
1134 if (assets.count(ob->asset_name)) {
1135 continue;
1136 }
1137 assets.insert(ob->asset_name);
1138 const uint32_t hash_asset = util_murmur_hash3(
1139 ob->asset_name.c_str(), ob->asset_name.length(), 0);
1140 manifest += string_printf("\"%s\":\"%08x\",", ob->asset_name.c_str(), hash_asset);
1141 }
1142 manifest[manifest.size() - 1] = '}';
1143 return manifest;
1144}
1145
unsigned int uint
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
list< Attribute > attributes
Attribute * find(ustring name) const
device_vector< DecomposedTransform > object_motion
Definition devicescene.h:43
device_vector< Transform > object_motion_pass
Definition devicescene.h:42
device_vector< float > volume_step_size
Definition devicescene.h:92
device_vector< uint > object_prim_offset
Definition devicescene.h:45
device_vector< uint > object_flag
Definition devicescene.h:44
KernelData data
Definition devicescene.h:94
device_vector< KernelObject > objects
Definition devicescene.h:41
virtual BVHLayoutMask get_bvh_layout_mask(const uint kernel_features) const =0
bool transform_applied
bool has_true_displacement() const
bool is_volume() const
bool is_pointcloud() const
bool is_hair() const
bool has_surface_bssrdf
virtual PrimitiveType primitive_type() const =0
size_t attr_map_offset
AttributeSet attributes
bool is_mesh() const
Definition hair.h:13
ImageMetaData metadata()
void tag_update(Scene *scene, const uint32_t flag)
@ EMISSIVE_MESH_MODIFIED
Definition scene/light.h:89
void tag_update(Scene *scene, const uint32_t flag)
string get_cryptomatte_objects(Scene *scene)
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress)
void device_update_object_transform(UpdateObjectTransformState *state, Object *ob, bool update_all, const Scene *scene)
void device_free(Device *device, DeviceScene *dscene, bool force_free)
string get_cryptomatte_assets(Scene *scene)
bool need_update() const
void device_update_geom_offsets(Device *device, DeviceScene *dscene, Scene *scene)
void device_update_prim_offsets(Device *device, DeviceScene *dscene, Scene *scene)
void device_update_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
void apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
void device_update_flags(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress, bool bounds_valid=true)
bool get_cancel() const
Definition progress.h:77
void set_status(const string &status_, const string &substatus_="")
Definition progress.h:248
bool has_volume_attribute_dependency
bool has_volume
EmissionSampling emission_sampling
bool has_volume_spatial_varying
size_t size() const
T * alloc(const size_t width, const size_t height=0)
void free_if_need_realloc(bool force_free)
size_t size() const
nullptr float
#define LIGHT_LINK_SET_MAX
#define OBJECT_MOTION_PASS_SIZE
#define LIGHTGROUP_NONE
#define LIGHT_LINK_MASK_ALL
#define SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility)
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define assert(assertion)
VecBase< float, D > normalize(VecOp< float, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
ccl_device_inline float object_volume_density(KernelGlobals kg, const int object)
ccl_device_inline uint particle_index(KernelGlobals kg, const int particle)
@ ATTR_STD_VOLUME_VELOCITY
@ ATTR_STD_MOTION_VERTEX_POSITION
@ PATH_RAY_TRANSMIT
@ PATH_RAY_VOLUME_SCATTER
@ PATH_RAY_GLOSSY
@ PATH_RAY_ALL_VISIBILITY
@ PATH_RAY_DIFFUSE
@ EMISSION_SAMPLING_NONE
@ SD_OBJECT_MOTION
@ SD_OBJECT_HAS_VOLUME_ATTRIBUTES
@ SD_OBJECT_HAS_VOLUME
@ SD_OBJECT_INTERSECTS_VOLUME
@ SD_OBJECT_NEGATIVE_SCALE
@ SD_OBJECT_HOLDOUT_MASK
@ SD_OBJECT_HAS_VOLUME_MOTION
@ SD_OBJECT_CAUSTICS_RECEIVER
@ SD_OBJECT_SHADOW_CATCHER
@ SD_OBJECT_TRANSFORM_APPLIED
@ SD_OBJECT_HAS_VERTEX_MOTION
@ SD_OBJECT_CAUSTICS_CASTER
@ BVH_LAYOUT_MULTI_HIPRT_EMBREE
@ BVH_LAYOUT_METAL
@ BVH_LAYOUT_MULTI_HIPRT
@ BVH_LAYOUT_HIPRT
@ BVH_LAYOUT_MULTI_METAL
@ BVH_LAYOUT_MULTI_METAL_EMBREE
@ ATTR_ELEMENT_VOXEL
@ LIGHT_BACKGROUND
#define LOG_INFO
Definition log.h:106
CCL_NAMESPACE_BEGIN ccl_device_inline float2 zero_float2()
Definition math_float2.h:13
ccl_device_inline float reduce_min(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 one_float3()
Definition math_float3.h:26
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
static ulong state[N]
uint32_t util_murmur_hash3(const void *key, const int len, const uint32_t seed)
float util_hash_to_float(const uint32_t hash)
static void update(bNodeTree *ntree)
float average(point a)
Definition node_math.h:144
#define SOCKET_POINT(name, ui_name, default_value,...)
Definition node_type.h:217
#define SOCKET_FLOAT(name, ui_name, default_value,...)
Definition node_type.h:211
#define SOCKET_INT(name, ui_name, default_value,...)
Definition node_type.h:205
#define SOCKET_NODE(name, ui_name, node_type,...)
Definition node_type.h:240
#define SOCKET_TRANSFORM(name, ui_name, default_value,...)
Definition node_type.h:225
#define SOCKET_UINT(name, ui_name, default_value,...)
Definition node_type.h:207
#define NODE_DEFINE(structname)
Definition node_type.h:152
#define SOCKET_COLOR(name, ui_name, default_value,...)
Definition node_type.h:213
#define SOCKET_TRANSFORM_ARRAY(name, ui_name, default_value,...)
Definition node_type.h:280
#define SOCKET_BOOLEAN(name, ui_name, default_value,...)
Definition node_type.h:203
#define SOCKET_POINT2(name, ui_name, default_value,...)
Definition node_type.h:221
#define SOCKET_STRING(name, ui_name, default_value,...)
Definition node_type.h:223
#define SOCKET_UINT64(name, ui_name, default_value,...)
Definition node_type.h:209
int BVHLayoutMask
Definition params.h:50
#define fminf
static float object_volume_density(const Transform &tfm, Geometry *geom)
static int object_num_motion_verts(Geometry *geom)
#define FLT_MAX
Definition stdcycles.h:14
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
AttributeElement element
ImageHandle & data_voxel()
BoundBox transformed(const Transform *tfm) const
Definition boundbox.h:134
__forceinline bool intersects(const BoundBox &other)
Definition boundbox.h:157
Transform tfm
uint64_t shadow_set_membership
Transform itfm
float dupli_uv[2]
float shadow_terminator_geometry_offset
float cryptomatte_asset
uint blocker_shadow_set
uint attribute_map_offset
uint receiver_light_set
uint64_t light_set_membership
float shadow_terminator_shading_offset
float dupli_generated[3]
float cryptomatte_object
AttributeSet subd_attributes
Definition scene/mesh.h:174
@ SUBDIVISION_NONE
Definition scene/mesh.h:118
static NodeType * add(const char *name, CreateFunc create, Type type=NONE, const NodeType *base=nullptr)
ustring name
Definition graph/node.h:177
bool is_modified() const
Node(const NodeType *type, ustring name=ustring())
void compute_bounds(bool motion_blur)
bool use_motion() const
NODE_DECLARE BoundBox bounds
size_t attr_map_offset
bool usable_as_light() const
float shadow_terminator_shading_offset
vector< ParamValue > attributes
void update_motion()
int get_device_index() const
int motion_step(const float time) const
void apply_transform(bool apply_to_motion)
float compute_volume_step_size() const
bool has_light_linking() const
float shadow_terminator_geometry_offset
bool is_traceable() const
uint visibility_for_tracing() const
void tag_update(Scene *scene)
float color[4]
~Object() override
float motion_time(const int step) const
bool has_shadow_linking() const
struct LightgroupMembership * lightgroup
bool intersects_volume
ParticleData * particles
unique_ptr< ObjectManager > object_manager
Definition scene.h:150
MotionType need_motion() const
Definition scene.cpp:410
unique_ptr< LightManager > light_manager
Definition scene.h:146
unique_ptr< SceneUpdateStats > update_stats
Definition scene.h:175
void tag_shadow_catcher_modified()
Definition scene.cpp:796
MotionType
Definition scene.h:185
@ MOTION_PASS
Definition scene.h:185
@ MOTION_BLUR
Definition scene.h:185
unique_ptr_vector< ParticleSystem > particle_systems
Definition scene.h:139
unique_ptr< GeometryManager > geometry_manager
Definition scene.h:149
unique_ptr_vector< Object > objects
Definition scene.h:141
Integrator * integrator
Definition scene.h:130
struct Object * camera
map< ustring, int > lightgroups
Definition scene.h:120
unique_ptr< VolumeManager > volume_manager
Definition scene.h:154
map< ParticleSystem *, int > particle_offset
DecomposedTransform * object_motion
Scene::MotionType need_motion
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
i
Definition text_draw.cc:230
void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, const size_t size)
ccl_device_inline Transform transform_identity()
Definition transform.h:322
ccl_device_inline Transform transform_empty()
Definition transform.h:411
ccl_device_inline bool transform_negative_scale(const Transform &tfm)
Definition transform.h:391
ccl_device void transform_motion_array_interpolate(ccl_private Transform *tfm, const ccl_global DecomposedTransform *motion, const uint numsteps, const float time)
Definition transform.h:591
ccl_device_inline Transform transform_inverse(const Transform tfm)
Definition transform.h:525
ccl_device_inline bool transform_uniform_scale(const Transform &tfm, float &scale)
Definition transform.h:368
ccl_device_inline float3 transform_direction(const ccl_private Transform *t, const float3 a)
Definition transform.h:127
wmTimer * timer
uint len
uint8_t flag
Definition wm_window.cc:145