Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
300 lines
9.4 KiB
C++
300 lines
9.4 KiB
C++
/* SPDX-FileCopyrightText: 2001-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BKE_light_linking.h"
|
|
|
|
#include <string>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_ID.h"
|
|
#include "DNA_collection_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BLI_assert.h"
|
|
#include "BLI_string.h"
|
|
|
|
#include "BKE_collection.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_report.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_build.h"
|
|
|
|
void BKE_light_linking_free_if_empty(Object *object)
|
|
{
|
|
if (object->light_linking->receiver_collection == nullptr &&
|
|
object->light_linking->blocker_collection == nullptr)
|
|
{
|
|
MEM_SAFE_FREE(object->light_linking);
|
|
}
|
|
}
|
|
|
|
Collection *BKE_light_linking_collection_get(const Object *object,
|
|
const LightLinkingType link_type)
|
|
{
|
|
if (!object->light_linking) {
|
|
return nullptr;
|
|
}
|
|
|
|
switch (link_type) {
|
|
case LIGHT_LINKING_RECEIVER:
|
|
return object->light_linking->receiver_collection;
|
|
case LIGHT_LINKING_BLOCKER:
|
|
return object->light_linking->blocker_collection;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static std::string get_default_collection_name(const Object *object,
|
|
const LightLinkingType link_type)
|
|
{
|
|
const char *format;
|
|
|
|
switch (link_type) {
|
|
case LIGHT_LINKING_RECEIVER:
|
|
format = DATA_("Light Linking for %s");
|
|
break;
|
|
case LIGHT_LINKING_BLOCKER:
|
|
format = DATA_("Shadow Linking for %s");
|
|
break;
|
|
}
|
|
|
|
char name[MAX_ID_NAME];
|
|
SNPRINTF(name, format, object->id.name + 2);
|
|
|
|
return name;
|
|
}
|
|
|
|
Collection *BKE_light_linking_collection_new(Main *bmain,
|
|
Object *object,
|
|
const LightLinkingType link_type)
|
|
{
|
|
const std::string collection_name = get_default_collection_name(object, link_type);
|
|
|
|
Collection *new_collection = BKE_collection_add(bmain, nullptr, collection_name.c_str());
|
|
|
|
BKE_light_linking_collection_assign(bmain, object, new_collection, link_type);
|
|
|
|
return new_collection;
|
|
}
|
|
|
|
void BKE_light_linking_collection_assign_only(Object *object,
|
|
Collection *new_collection,
|
|
const LightLinkingType link_type)
|
|
{
|
|
/* Remove user from old collection. */
|
|
Collection *old_collection = BKE_light_linking_collection_get(object, link_type);
|
|
if (old_collection) {
|
|
id_us_min(&old_collection->id);
|
|
}
|
|
|
|
/* Allocate light linking on demand. */
|
|
if (new_collection && !object->light_linking) {
|
|
object->light_linking = MEM_cnew<LightLinking>(__func__);
|
|
}
|
|
|
|
if (object->light_linking) {
|
|
/* Assign and increment user of new collection. */
|
|
switch (link_type) {
|
|
case LIGHT_LINKING_RECEIVER:
|
|
object->light_linking->receiver_collection = new_collection;
|
|
break;
|
|
case LIGHT_LINKING_BLOCKER:
|
|
object->light_linking->blocker_collection = new_collection;
|
|
break;
|
|
default:
|
|
BLI_assert_unreachable();
|
|
break;
|
|
}
|
|
|
|
if (new_collection) {
|
|
id_us_plus(&new_collection->id);
|
|
}
|
|
|
|
BKE_light_linking_free_if_empty(object);
|
|
}
|
|
}
|
|
|
|
void BKE_light_linking_collection_assign(Main *bmain,
|
|
Object *object,
|
|
Collection *new_collection,
|
|
const LightLinkingType link_type)
|
|
{
|
|
BKE_light_linking_collection_assign_only(object, new_collection, link_type);
|
|
|
|
DEG_id_tag_update(&object->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SHADING);
|
|
DEG_relations_tag_update(bmain);
|
|
}
|
|
|
|
/* Add object to the light linking collection and return corresponding CollectionLightLinking
|
|
* settings.
|
|
*
|
|
* If the object is already in the collection then the content of the collection is not modified,
|
|
* and the existing light linking settings are returned. */
|
|
static CollectionLightLinking *light_linking_collection_add_object(Main *bmain,
|
|
Collection *collection,
|
|
Object *object)
|
|
{
|
|
BKE_collection_object_add(bmain, collection, object);
|
|
|
|
LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
|
|
if (collection_object->ob == object) {
|
|
return &collection_object->light_linking;
|
|
}
|
|
}
|
|
|
|
BLI_assert_msg(0, "Object was not found after added to the light linking collection");
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/* Add child collection to the light linking collection and return corresponding
|
|
* CollectionLightLinking settings.
|
|
*
|
|
* If the child collection is already in the collection then the content of the collection is
|
|
* not modified, and the existing light linking settings are returned. */
|
|
static CollectionLightLinking *light_linking_collection_add_collection(Main *bmain,
|
|
Collection *collection,
|
|
Collection *child)
|
|
{
|
|
BKE_collection_child_add(bmain, collection, child);
|
|
|
|
LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
|
|
if (collection_child->collection == child) {
|
|
return &collection_child->light_linking;
|
|
}
|
|
}
|
|
|
|
BLI_assert_msg(0, "Collection was not found after added to the light linking collection");
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void BKE_light_linking_add_receiver_to_collection(Main *bmain,
|
|
Collection *collection,
|
|
ID *receiver,
|
|
const eCollectionLightLinkingState link_state)
|
|
{
|
|
const ID_Type id_type = GS(receiver->name);
|
|
|
|
CollectionLightLinking *collection_light_linking = nullptr;
|
|
|
|
if (id_type == ID_OB) {
|
|
Object *object = reinterpret_cast<Object *>(receiver);
|
|
if (!OB_TYPE_IS_GEOMETRY(object->type)) {
|
|
return;
|
|
}
|
|
collection_light_linking = light_linking_collection_add_object(bmain, collection, object);
|
|
}
|
|
else if (id_type == ID_GR) {
|
|
collection_light_linking = light_linking_collection_add_collection(
|
|
bmain, collection, reinterpret_cast<Collection *>(receiver));
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
|
|
if (!collection_light_linking) {
|
|
return;
|
|
}
|
|
|
|
collection_light_linking->link_state = link_state;
|
|
|
|
DEG_id_tag_update(&collection->id, ID_RECALC_HIERARCHY);
|
|
DEG_id_tag_update(receiver, ID_RECALC_SHADING);
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
}
|
|
|
|
bool BKE_light_linking_unlink_id_from_collection(Main *bmain,
|
|
Collection *collection,
|
|
ID *id,
|
|
ReportList *reports)
|
|
{
|
|
const ID_Type id_type = GS(id->name);
|
|
|
|
if (id_type == ID_OB) {
|
|
BKE_collection_object_remove(bmain, collection, reinterpret_cast<Object *>(id), false);
|
|
}
|
|
else if (id_type == ID_GR) {
|
|
BKE_collection_child_remove(bmain, collection, reinterpret_cast<Collection *>(id));
|
|
}
|
|
else {
|
|
BKE_reportf(reports,
|
|
RPT_ERROR,
|
|
"Cannot unlink unsupported '%s' from light linking collection '%s'",
|
|
id->name + 2,
|
|
collection->id.name + 2);
|
|
return false;
|
|
}
|
|
|
|
DEG_id_tag_update(&collection->id, ID_RECALC_HIERARCHY);
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
return true;
|
|
}
|
|
|
|
void BKE_light_linking_link_receiver_to_emitter(Main *bmain,
|
|
Object *emitter,
|
|
Object *receiver,
|
|
const LightLinkingType link_type,
|
|
const eCollectionLightLinkingState link_state)
|
|
{
|
|
if (!OB_TYPE_IS_GEOMETRY(receiver->type)) {
|
|
return;
|
|
}
|
|
|
|
Collection *collection = BKE_light_linking_collection_get(emitter, link_type);
|
|
|
|
if (!collection) {
|
|
collection = BKE_light_linking_collection_new(bmain, emitter, link_type);
|
|
}
|
|
|
|
BKE_light_linking_add_receiver_to_collection(bmain, collection, &receiver->id, link_state);
|
|
}
|
|
|
|
void BKE_light_linking_select_receivers_of_emitter(Scene *scene,
|
|
ViewLayer *view_layer,
|
|
Object *emitter,
|
|
const LightLinkingType link_type)
|
|
{
|
|
Collection *collection = BKE_light_linking_collection_get(emitter, link_type);
|
|
if (!collection) {
|
|
return;
|
|
}
|
|
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
|
|
|
/* Deselect all currently selected objects in the view layer, but keep the emitter selected.
|
|
* This is because the operation is called from the emitter being active, and it will be
|
|
* confusing to deselect it but keep active. */
|
|
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
|
|
if (base->object == emitter) {
|
|
continue;
|
|
}
|
|
base->flag &= ~BASE_SELECTED;
|
|
}
|
|
|
|
/* Select objects which are reachable via the receiver collection hierarchy. */
|
|
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
|
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
|
|
if (!base) {
|
|
continue;
|
|
}
|
|
|
|
/* TODO(sergey): Check whether the object is configured to receive light. */
|
|
|
|
base->flag |= BASE_SELECTED;
|
|
}
|
|
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
|
|
}
|