Blender V5.0
light_linking.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_light_linking.h"
6
7#include <string>
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_ID.h"
13#include "DNA_object_types.h"
14#include "DNA_scene_types.h"
15
16#include "BLI_assert.h"
17#include "BLI_listbase.h"
18#include "BLI_string_utf8.h"
19
20#include "BKE_collection.hh"
21#include "BKE_layer.hh"
22#include "BKE_lib_id.hh"
23#include "BKE_report.hh"
24
25#include "BLT_translation.hh"
26
27#include "DEG_depsgraph.hh"
29
31{
32 if (object->light_linking == nullptr) {
33 object->light_linking = MEM_callocN<LightLinking>(__func__);
34 }
35}
36
37void BKE_light_linking_copy(Object *object_dst, const Object *object_src, const int copy_flags)
38{
39 BLI_assert(ELEM(object_dst->light_linking, nullptr, object_src->light_linking));
40 if (object_src->light_linking) {
41 object_dst->light_linking = MEM_dupallocN<LightLinking>(__func__,
42 *(object_src->light_linking));
43 if ((copy_flags & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
44 id_us_plus(blender::id_cast<ID *>(object_dst->light_linking->receiver_collection));
45 id_us_plus(blender::id_cast<ID *>(object_dst->light_linking->blocker_collection));
46 }
47 }
48}
49
50void BKE_light_linking_delete(Object *object, const int delete_flags)
51{
52 if (object->light_linking) {
53 if ((delete_flags & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
54 id_us_min(blender::id_cast<ID *>(object->light_linking->receiver_collection));
55 id_us_min(blender::id_cast<ID *>(object->light_linking->blocker_collection));
56 }
58 }
59}
60
62{
63 if (object->light_linking->receiver_collection == nullptr &&
64 object->light_linking->blocker_collection == nullptr)
65 {
67 }
68}
69
71 const LightLinkingType link_type)
72{
73 if (!object->light_linking) {
74 return nullptr;
75 }
76
77 switch (link_type) {
79 return object->light_linking->receiver_collection;
81 return object->light_linking->blocker_collection;
82 }
83
84 return nullptr;
85}
86
87static std::string get_default_collection_name(const Object *object,
88 const LightLinkingType link_type)
89{
90 const char *format;
91
92 switch (link_type) {
94 format = DATA_("Light Linking for %s");
95 break;
97 format = DATA_("Shadow Linking for %s");
98 break;
99 }
100
101 char name[MAX_ID_NAME - 2];
102 SNPRINTF_UTF8(name, format, object->id.name + 2);
103
104 return name;
105}
106
108 Object *object,
109 const LightLinkingType link_type)
110{
111 const std::string collection_name = get_default_collection_name(object, link_type);
112
113 Collection *new_collection = BKE_collection_add(bmain, nullptr, collection_name.c_str());
114
115 BKE_light_linking_collection_assign(bmain, object, new_collection, link_type);
116
117 return new_collection;
118}
119
121 Collection *new_collection,
122 const LightLinkingType link_type)
123{
124 /* Remove user from old collection. */
125 Collection *old_collection = BKE_light_linking_collection_get(object, link_type);
126 if (old_collection) {
127 id_us_min(&old_collection->id);
128 }
129
130 /* Allocate light linking on demand. */
131 if (new_collection) {
133 }
134
135 if (object->light_linking) {
136 /* Assign and increment user of new collection. */
137 switch (link_type) {
139 object->light_linking->receiver_collection = new_collection;
140 break;
142 object->light_linking->blocker_collection = new_collection;
143 break;
144 default:
146 break;
147 }
148
149 if (new_collection) {
150 id_us_plus(&new_collection->id);
151 }
152
154 }
155}
156
158 Object *object,
159 Collection *new_collection,
160 const LightLinkingType link_type)
161{
162 BKE_light_linking_collection_assign_only(object, new_collection, link_type);
163
166}
167
168static CollectionObject *find_collection_object(const Collection *collection, const Object *object)
169{
170 LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
171 if (collection_object->ob == object) {
172 return collection_object;
173 }
174 }
175
176 return nullptr;
177}
178
180 const Collection *child)
181{
182 LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
183 if (collection_child->collection == child) {
184 return collection_child;
185 }
186 }
187
188 return nullptr;
189}
190
191/* Add object to the light linking collection and return corresponding CollectionLightLinking
192 * settings.
193 *
194 * If the object is already in the collection then the content of the collection is not modified,
195 * and the existing light linking settings are returned. */
197 Collection *collection,
198 Object *object)
199{
200 BKE_collection_object_add(bmain, collection, object);
201
202 CollectionObject *collection_object = find_collection_object(collection, object);
203
204 if (!collection_object) {
205 BLI_assert_msg(0, "Object was not found after added to the light linking collection");
206 return nullptr;
207 }
208
209 return &collection_object->light_linking;
210}
211
212/* Add child collection to the light linking collection and return corresponding
213 * CollectionLightLinking settings.
214 *
215 * If the child collection is already in the collection then the content of the collection is
216 * not modified, and the existing light linking settings are returned. */
218 Collection *collection,
219 Collection *child)
220{
221 BKE_collection_child_add(bmain, collection, child);
222
223 CollectionChild *collection_child = find_collection_child(collection, child);
224
225 if (!collection_child) {
226 BLI_assert_msg(0, "Collection was not found after added to the light linking collection");
227 return nullptr;
228 }
229
230 return &collection_child->light_linking;
231}
232
234 Collection *collection,
235 ID *receiver,
236 const eCollectionLightLinkingState link_state)
237{
238 const ID_Type id_type = GS(receiver->name);
239
240 CollectionLightLinking *collection_light_linking = nullptr;
241
242 if (id_type == ID_OB) {
243 Object *object = reinterpret_cast<Object *>(receiver);
244
245 if (object->type == OB_EMPTY && object->instance_collection) {
247 return;
248 }
249 }
250 else if (!OB_TYPE_IS_GEOMETRY(object->type)) {
251 return;
252 }
253 collection_light_linking = light_linking_collection_add_object(bmain, collection, object);
254 }
255 else if (id_type == ID_GR) {
256 collection_light_linking = light_linking_collection_add_collection(
257 bmain, collection, reinterpret_cast<Collection *>(receiver));
258 }
259 else {
260 return;
261 }
262
263 if (!collection_light_linking) {
264 return;
265 }
266
267 collection_light_linking->link_state = link_state;
268
271
273}
274
276 Collection *receiver,
277 const ID *before)
278{
279 CollectionChild *receiver_collection_child = find_collection_child(collection, receiver);
280 if (!receiver_collection_child) {
281 BLI_assert_msg(0, "Receiver child was not found after adding collection to light linking");
282 return;
283 }
284
285 const ID_Type before_id_type = GS(before->name);
286
287 if (before_id_type != ID_GR) {
288 /* Adding before object: move the collection to the very bottom.
289 * This is as far to the bottom as the receiver can be in the flattened list of the collection.
290 */
291 BLI_remlink(&collection->children, receiver_collection_child);
292 BLI_addtail(&collection->children, receiver_collection_child);
293 return;
294 }
295
296 CollectionChild *before_collection_child = find_collection_child(
297 collection, reinterpret_cast<const Collection *>(before));
298 if (!before_collection_child) {
299 BLI_assert_msg(0, "Before child was not found");
300 return;
301 }
302
303 BLI_remlink(&collection->children, receiver_collection_child);
304 BLI_insertlinkbefore(&collection->children, before_collection_child, receiver_collection_child);
305}
306
308 Collection *receiver,
309 const ID *after)
310{
311 CollectionChild *receiver_collection_child = find_collection_child(collection, receiver);
312 if (!receiver_collection_child) {
313 BLI_assert_msg(0, "Receiver child was not found after adding collection to light linking");
314 return;
315 }
316
317 const ID_Type after_id_type = GS(after->name);
318
319 if (after_id_type != ID_GR) {
320 /* Adding before object: move the collection to the very bottom.
321 * This is as far to the bottom as the receiver can be in the flattened list of the collection.
322 */
323 BLI_remlink(&collection->children, receiver_collection_child);
324 BLI_addtail(&collection->children, receiver_collection_child);
325 return;
326 }
327
328 CollectionChild *after_collection_child = find_collection_child(
329 collection, reinterpret_cast<const Collection *>(after));
330 if (!after_collection_child) {
331 BLI_assert_msg(0, "After child was not found");
332 return;
333 }
334
335 BLI_remlink(&collection->children, receiver_collection_child);
336 BLI_insertlinkafter(&collection->children, after_collection_child, receiver_collection_child);
337}
338
340 Object *receiver,
341 const ID *before)
342{
343 CollectionObject *receiver_collection_object = find_collection_object(collection, receiver);
344 if (!receiver_collection_object) {
346 0, "Receiver collection object was not found after adding collection to light linking");
347 return;
348 }
349
350 const ID_Type before_id_type = GS(before->name);
351
352 if (before_id_type != ID_OB) {
353 /* Adding before collection: move the receiver to the very beginning of the child objects list.
354 * This is as close to the top of the flattened list of the collection content the object can
355 * possibly be. */
356 BLI_remlink(&collection->gobject, receiver_collection_object);
357 BLI_addhead(&collection->gobject, receiver_collection_object);
358 return;
359 }
360
361 CollectionObject *before_collection_object = find_collection_object(
362 collection, reinterpret_cast<const Object *>(before));
363 if (!before_collection_object) {
364 BLI_assert_msg(0, "Before collection object was not found");
365 return;
366 }
367
368 BLI_remlink(&collection->gobject, receiver_collection_object);
369 BLI_insertlinkbefore(&collection->gobject, before_collection_object, receiver_collection_object);
370}
371
372static void order_object_receiver_after(Collection *collection, Object *receiver, const ID *after)
373{
374 CollectionObject *receiver_collection_object = find_collection_object(collection, receiver);
375 if (!receiver_collection_object) {
377 0, "Receiver collection object was not found after adding collection to light linking");
378 return;
379 }
380
381 const ID_Type after_id_type = GS(after->name);
382
383 if (after_id_type != ID_OB) {
384 /* Adding after collection: move the receiver to the very beginning of the child objects list.
385 * This is as close to the top of the flattened list of the collection content the object can
386 * possibly be. */
387 BLI_remlink(&collection->gobject, receiver_collection_object);
388 BLI_addhead(&collection->gobject, receiver_collection_object);
389 return;
390 }
391
392 CollectionObject *after_collection_object = find_collection_object(
393 collection, reinterpret_cast<const Object *>(after));
394 if (!after_collection_object) {
395 BLI_assert_msg(0, "After collection object was not found");
396 return;
397 }
398
399 BLI_remlink(&collection->gobject, receiver_collection_object);
400 BLI_insertlinkafter(&collection->gobject, after_collection_object, receiver_collection_object);
401}
402
404 Main *bmain,
405 Collection *collection,
406 ID *receiver,
407 const ID *before,
408 const eCollectionLightLinkingState link_state)
409{
410 BLI_assert(before);
411
412 BKE_light_linking_add_receiver_to_collection(bmain, collection, receiver, link_state);
413
414 if (!before) {
415 return;
416 }
417
418 const ID_Type id_type = GS(receiver->name);
419 if (id_type == ID_OB) {
420 order_object_receiver_before(collection, reinterpret_cast<Object *>(receiver), before);
421 }
422 else if (id_type == ID_GR) {
423 order_collection_receiver_before(collection, reinterpret_cast<Collection *>(receiver), before);
424 }
425}
426
428 Main *bmain,
429 Collection *collection,
430 ID *receiver,
431 const ID *after,
432 const eCollectionLightLinkingState link_state)
433{
434 BLI_assert(after);
435
436 BKE_light_linking_add_receiver_to_collection(bmain, collection, receiver, link_state);
437
438 if (!after) {
439 return;
440 }
441
442 const ID_Type id_type = GS(receiver->name);
443 if (id_type == ID_OB) {
444 order_object_receiver_after(collection, reinterpret_cast<Object *>(receiver), after);
445 }
446 else if (id_type == ID_GR) {
447 order_collection_receiver_after(collection, reinterpret_cast<Collection *>(receiver), after);
448 }
449}
450
452 Collection *collection,
453 ID *id,
454 ReportList *reports)
455{
456 const ID_Type id_type = GS(id->name);
457
458 if (id_type == ID_OB) {
459 BKE_collection_object_remove(bmain, collection, reinterpret_cast<Object *>(id), false);
460 }
461 else if (id_type == ID_GR) {
462 BKE_collection_child_remove(bmain, collection, reinterpret_cast<Collection *>(id));
463 }
464 else {
465 BKE_reportf(reports,
466 RPT_ERROR,
467 "Cannot unlink unsupported '%s' from light linking collection '%s'",
468 id->name + 2,
469 collection->id.name + 2);
470 return false;
471 }
472
474 if (id_type == ID_OB) {
476 }
477
479
480 return true;
481}
482
484 Object *emitter,
485 Object *receiver,
486 const LightLinkingType link_type,
487 const eCollectionLightLinkingState link_state)
488{
489 if (receiver->type == OB_EMPTY && receiver->instance_collection) {
491 return;
492 }
493 }
494 else if (!OB_TYPE_IS_GEOMETRY(receiver->type)) {
495 return;
496 }
497
498 Collection *collection = BKE_light_linking_collection_get(emitter, link_type);
499
500 if (!collection) {
501 collection = BKE_light_linking_collection_new(bmain, emitter, link_type);
502 }
503
504 BKE_light_linking_add_receiver_to_collection(bmain, collection, &receiver->id, link_state);
505}
506
508 ViewLayer *view_layer,
509 Object *emitter,
510 const LightLinkingType link_type)
511{
512 Collection *collection = BKE_light_linking_collection_get(emitter, link_type);
513 if (!collection) {
514 return;
515 }
516
517 BKE_view_layer_synced_ensure(scene, view_layer);
518
519 /* Deselect all currently selected objects in the view layer, but keep the emitter selected.
520 * This is because the operation is called from the emitter being active, and it will be
521 * confusing to deselect it but keep active. */
523 if (base->object == emitter) {
524 continue;
525 }
526 base->flag &= ~BASE_SELECTED;
527 }
528
529 /* Select objects which are reachable via the receiver collection hierarchy. */
530 LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
531 Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
532 if (!base) {
533 continue;
534 }
535
536 /* TODO(sergey): Check whether the object is configured to receive light. */
537
538 base->flag |= BASE_SELECTED;
539 }
540
542}
bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, bool free_us)
bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child)
Collection * BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child)
bool BKE_collection_contains_geometry_recursive(const Collection *collection)
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
void id_us_plus(ID *id)
Definition lib_id.cc:358
@ LIB_ID_CREATE_NO_USER_REFCOUNT
void id_us_min(ID *id)
Definition lib_id.cc:366
LightLinkingType
@ LIGHT_LINKING_BLOCKER
@ LIGHT_LINKING_RECEIVER
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
#define SNPRINTF_UTF8(dst, format,...)
#define ELEM(...)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_SHADING
Definition DNA_ID.h:1094
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_HIERARCHY
Definition DNA_ID.h:1158
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
#define MAX_ID_NAME
Definition DNA_ID.h:373
ID_Type
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
eCollectionLightLinkingState
Object is a sort of wrapper for general info.
#define OB_TYPE_IS_GEOMETRY(_type)
@ OB_EMPTY
#define BASE_SELECTED(v3d, base)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define GS(x)
void BKE_light_linking_add_receiver_to_collection_before(Main *bmain, Collection *collection, ID *receiver, const ID *before, const eCollectionLightLinkingState link_state)
static CollectionObject * find_collection_object(const Collection *collection, const Object *object)
void BKE_light_linking_copy(Object *object_dst, const Object *object_src, const int copy_flags)
Collection * BKE_light_linking_collection_get(const Object *object, const LightLinkingType link_type)
static CollectionLightLinking * light_linking_collection_add_collection(Main *bmain, Collection *collection, Collection *child)
void BKE_light_linking_collection_assign_only(Object *object, Collection *new_collection, const LightLinkingType link_type)
void BKE_light_linking_free_if_empty(Object *object)
bool BKE_light_linking_unlink_id_from_collection(Main *bmain, Collection *collection, ID *id, ReportList *reports)
void BKE_light_linking_add_receiver_to_collection_after(Main *bmain, Collection *collection, ID *receiver, const ID *after, const eCollectionLightLinkingState link_state)
static void order_collection_receiver_before(Collection *collection, Collection *receiver, const ID *before)
static CollectionChild * find_collection_child(const Collection *collection, const Collection *child)
Collection * BKE_light_linking_collection_new(Main *bmain, Object *object, const LightLinkingType link_type)
static CollectionLightLinking * light_linking_collection_add_object(Main *bmain, Collection *collection, Object *object)
void BKE_light_linking_add_receiver_to_collection(Main *bmain, Collection *collection, ID *receiver, const eCollectionLightLinkingState link_state)
void BKE_light_linking_select_receivers_of_emitter(Scene *scene, ViewLayer *view_layer, Object *emitter, const LightLinkingType link_type)
static std::string get_default_collection_name(const Object *object, const LightLinkingType link_type)
void BKE_light_linking_delete(Object *object, const int delete_flags)
static void order_object_receiver_before(Collection *collection, Object *receiver, const ID *before)
void BKE_light_linking_ensure(Object *object)
void BKE_light_linking_collection_assign(Main *bmain, Object *object, Collection *new_collection, const LightLinkingType link_type)
static void order_object_receiver_after(Collection *collection, Object *receiver, const ID *after)
static void order_collection_receiver_after(Collection *collection, Collection *receiver, const ID *after)
void BKE_light_linking_link_receiver_to_emitter(Main *bmain, Object *emitter, Object *receiver, const LightLinkingType link_type, const eCollectionLightLinkingState link_state)
format
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
const char * name
short flag
CollectionLightLinking light_linking
CollectionLightLinking light_linking
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
struct Collection * blocker_collection
struct Collection * receiver_collection
struct Collection * instance_collection
LightLinking * light_linking