Cycles: Rudimentary utilities to plot light tree
Plotting happens from the given root node into a graphviz file. Supports plotting from both scene level LightTreeNode and the kernel level KernelLightTreeNode. An external graphviz command is to be used to convert generated file to an image. Pull Request: https://projects.blender.org/blender/blender/pulls/134738
This commit is contained in:
committed by
Sergey Sharybin
parent
20ec789889
commit
0a59c7f0ea
@@ -30,6 +30,7 @@ set(SRC
|
||||
integrator.cpp
|
||||
light.cpp
|
||||
light_tree.cpp
|
||||
light_tree_debug.cpp
|
||||
mesh.cpp
|
||||
mesh_displace.cpp
|
||||
mesh_subdivision.cpp
|
||||
@@ -71,6 +72,7 @@ set(SRC_HEADERS
|
||||
integrator.h
|
||||
light.h
|
||||
light_tree.h
|
||||
light_tree_debug.h
|
||||
mesh.h
|
||||
object.h
|
||||
osl.h
|
||||
|
||||
330
intern/cycles/scene/light_tree_debug.cpp
Normal file
330
intern/cycles/scene/light_tree_debug.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/light_tree_debug.h"
|
||||
|
||||
#include "scene/light.h"
|
||||
#include "scene/light_tree.h"
|
||||
#include "scene/object.h"
|
||||
#include "scene/scene.h"
|
||||
#include "util/path.h"
|
||||
#include "util/string.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static string get_node_id(const LightTreeNode &node)
|
||||
{
|
||||
return string_printf("node@%p", &node);
|
||||
}
|
||||
|
||||
static string get_emitter_id(const LightTreeEmitter &emitter)
|
||||
{
|
||||
return string_printf("emitter@%p", &emitter);
|
||||
}
|
||||
|
||||
static string set_membership_str(const uint64_t set_membership)
|
||||
{
|
||||
if (set_membership == ~uint64_t(0)) {
|
||||
return "ALL";
|
||||
}
|
||||
return std::to_string(set_membership);
|
||||
}
|
||||
|
||||
static void recursive_print_node(FILE *file, const LightTreeNode &node)
|
||||
{
|
||||
int field = 0;
|
||||
|
||||
string label = string_printf("<f%d> node @%p", field++, &node);
|
||||
if (node.is_instance()) {
|
||||
label += string_printf("| <f%d> instance", field++);
|
||||
}
|
||||
if (node.is_leaf()) {
|
||||
label += string_printf("| <f%d> leaf", field++);
|
||||
}
|
||||
if (node.is_inner()) {
|
||||
label += string_printf("| <f%d> inner", field++);
|
||||
}
|
||||
if (node.is_distant()) {
|
||||
label += string_printf("| <f%d> distant", field++);
|
||||
}
|
||||
|
||||
if (node.light_link.set_membership == ~uint64_t(0)) {
|
||||
label += string_printf("| <f%d> set membership:ALL", field++);
|
||||
}
|
||||
else {
|
||||
label += string_printf("| <f%d> set membership:%s",
|
||||
field++,
|
||||
set_membership_str(node.light_link.set_membership).c_str());
|
||||
}
|
||||
|
||||
label += string_printf(
|
||||
"| <f%d> shareable:%s", field++, node.light_link.shareable ? "True" : "False");
|
||||
|
||||
if (node.is_inner()) {
|
||||
label += string_printf("| <left> left");
|
||||
label += string_printf("| <right> right");
|
||||
}
|
||||
else if (node.is_leaf()) {
|
||||
label += string_printf("| <emitters> emitters");
|
||||
}
|
||||
|
||||
fprintf(file, "\"%s\" [\n", get_node_id(node).c_str());
|
||||
fprintf(file, " label = \"%s\"\n", label.c_str());
|
||||
fprintf(file, " shape = \"record\"\n");
|
||||
fprintf(file, "];\n");
|
||||
|
||||
if (node.is_inner()) {
|
||||
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get();
|
||||
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get();
|
||||
|
||||
recursive_print_node(file, left_node);
|
||||
recursive_print_node(file, right_node);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_emitters(FILE *file, const Scene &scene, const LightTree &tree)
|
||||
{
|
||||
const size_t num_emitters = tree.num_emitters();
|
||||
const LightTreeEmitter *emitters = tree.get_emitters();
|
||||
for (size_t i = 0; i < num_emitters; ++i) {
|
||||
const LightTreeEmitter &emitter = emitters[i];
|
||||
|
||||
int field = 0;
|
||||
|
||||
string label = string_printf("<f%d> emitter %s", field++, std::to_string(i).c_str());
|
||||
|
||||
/* Emitter details (type, object or light name). */
|
||||
if (emitter.is_light()) {
|
||||
const Light &lamp = *scene.lights[emitter.object_id];
|
||||
label += string_printf("|<f%d> light", field++);
|
||||
label += string_printf("|<f%d> %s", field++, lamp.name.c_str());
|
||||
}
|
||||
else if (emitter.is_triangle()) {
|
||||
const Object &object = *scene.objects[emitter.object_id];
|
||||
label += string_printf("|<f%d> triangle", field++);
|
||||
label += string_printf("|<f%d> %s", field++, object.name.c_str());
|
||||
}
|
||||
else if (emitter.is_mesh()) {
|
||||
const Object &object = *scene.objects[emitter.object_id];
|
||||
label += string_printf("|<f%d> mesh", field++);
|
||||
label += string_printf("|<f%d> %s", field++, object.name.c_str());
|
||||
}
|
||||
|
||||
/* Light linking. */
|
||||
if (emitter.light_set_membership == ~uint64_t(0)) {
|
||||
label += string_printf("|<f%d> set membership:ALL", field++);
|
||||
}
|
||||
else {
|
||||
label += string_printf("|<f%d> set membership:%s",
|
||||
field++,
|
||||
set_membership_str(emitter.light_set_membership).c_str());
|
||||
}
|
||||
|
||||
/* Bounding box. */
|
||||
label += string_printf("|<f%d> bbox.min (%f %f %f)",
|
||||
field++,
|
||||
double(emitter.measure.bbox.min.x),
|
||||
double(emitter.measure.bbox.min.y),
|
||||
double(emitter.measure.bbox.min.z));
|
||||
label += string_printf("|<f%d> bbox.max (%f %f %f)",
|
||||
field++,
|
||||
double(emitter.measure.bbox.max.x),
|
||||
double(emitter.measure.bbox.max.y),
|
||||
double(emitter.measure.bbox.max.z));
|
||||
|
||||
/* Orientation bounds. */
|
||||
label += string_printf("|<f%d> bcone.axis (%f %f %f)",
|
||||
field++,
|
||||
double(emitter.measure.bcone.axis.x),
|
||||
double(emitter.measure.bcone.axis.y),
|
||||
double(emitter.measure.bcone.axis.z));
|
||||
label += string_printf("|<f%d> theta_o %f, theta_e %f",
|
||||
field++,
|
||||
double(emitter.measure.bcone.theta_o),
|
||||
double(emitter.measure.bcone.theta_e));
|
||||
|
||||
/* Print node to the file. */
|
||||
fprintf(file, "\"%s\" [\n", get_emitter_id(emitter).c_str());
|
||||
fprintf(file, " label = \"%s\"\n", label.c_str());
|
||||
fprintf(file, " shape = \"record\"\n");
|
||||
fprintf(file, "];\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void recursive_print_node_relations(FILE *file,
|
||||
const LightTree &tree,
|
||||
const LightTreeNode &node,
|
||||
int &relation_id)
|
||||
{
|
||||
const string from_node_id = get_node_id(node);
|
||||
|
||||
if (node.is_leaf() || node.is_distant()) {
|
||||
const LightTreeEmitter *emitters = tree.get_emitters();
|
||||
for (int i = 0; i < node.get_leaf().num_emitters; i++) {
|
||||
const LightTreeEmitter &emitter = emitters[node.get_leaf().first_emitter_index + i];
|
||||
fprintf(file,
|
||||
"\"%s\":<emitters> -> \"%s\":f0 [ id = %d ];\n",
|
||||
from_node_id.c_str(),
|
||||
get_emitter_id(emitter).c_str(),
|
||||
relation_id++);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.is_inner()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get();
|
||||
const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get();
|
||||
|
||||
const string left_node_id = get_node_id(left_node);
|
||||
const string right_node_id = get_node_id(right_node);
|
||||
|
||||
fprintf(file,
|
||||
"\"%s\":left -> \"%s\":f0 [ id = %d ];\n",
|
||||
from_node_id.c_str(),
|
||||
left_node_id.c_str(),
|
||||
relation_id++);
|
||||
|
||||
fprintf(file,
|
||||
"\"%s\":right -> \"%s\":f0 [ id = %d ];\n",
|
||||
from_node_id.c_str(),
|
||||
right_node_id.c_str(),
|
||||
relation_id++);
|
||||
|
||||
recursive_print_node_relations(file, tree, left_node, relation_id);
|
||||
recursive_print_node_relations(file, tree, right_node, relation_id);
|
||||
}
|
||||
|
||||
void light_tree_plot_to_file(const Scene &scene,
|
||||
const LightTree &tree,
|
||||
const LightTreeNode &root_node,
|
||||
const string &filename)
|
||||
{
|
||||
FILE *file = path_fopen(filename, "w");
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
int relation_id = 0;
|
||||
|
||||
fprintf(file, "digraph g {\n");
|
||||
fprintf(file, "graph [\n");
|
||||
fprintf(file, " rankdir = \"LR\"\n");
|
||||
fprintf(file, "];\n");
|
||||
recursive_print_node(file, root_node);
|
||||
print_emitters(file, scene, tree);
|
||||
recursive_print_node_relations(file, tree, root_node, relation_id);
|
||||
fprintf(file, "}\n");
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
static string get_knode_id(const KernelLightTreeNode &knode)
|
||||
{
|
||||
return string_printf("knode@%p", &knode);
|
||||
}
|
||||
|
||||
static void recursive_print_knode(FILE *file,
|
||||
const uint knode_index,
|
||||
const KernelLightTreeNode *knodes,
|
||||
int &relation_id)
|
||||
{
|
||||
const KernelLightTreeNode &knode = knodes[knode_index];
|
||||
|
||||
/* The distant node is also considered o leaf node. */
|
||||
const bool is_leaf = knode.type >= LIGHT_TREE_LEAF;
|
||||
const bool is_inner = !is_leaf;
|
||||
|
||||
assert(!is_inner || knode.type != LIGHT_TREE_INSTANCE);
|
||||
|
||||
int field = 0;
|
||||
|
||||
string label = string_printf("<f%d> knode %u", field++, knode_index);
|
||||
|
||||
/* Bounding box. */
|
||||
label += string_printf("|<f%d> min (%f %f %f)",
|
||||
field++,
|
||||
double(knode.bbox.min.x),
|
||||
double(knode.bbox.min.y),
|
||||
double(knode.bbox.min.z));
|
||||
label += string_printf("|<f%d> max (%f %f %f)",
|
||||
field++,
|
||||
double(knode.bbox.max.x),
|
||||
double(knode.bbox.max.y),
|
||||
double(knode.bbox.max.z));
|
||||
|
||||
/* Orientation bounds. */
|
||||
label += string_printf("|<f%d> bcone.axis (%f %f %f)",
|
||||
field++,
|
||||
double(knode.bcone.axis.x),
|
||||
double(knode.bcone.axis.y),
|
||||
double(knode.bcone.axis.z));
|
||||
label += string_printf("|<f%d> theta_o %f, theta_e %f",
|
||||
field++,
|
||||
double(knode.bcone.theta_o),
|
||||
double(knode.bcone.theta_e));
|
||||
|
||||
label += string_printf("|<f%d> energy %f", field++, knode.energy);
|
||||
|
||||
if (is_leaf) {
|
||||
label += string_printf("|<f%d> first emitter %d", field++, knode.leaf.first_emitter);
|
||||
label += string_printf("|<f%d> num emitters %d", field++, knode.num_emitters);
|
||||
}
|
||||
|
||||
label += string_printf("|<f%d> bit trail %u", field++, knode.bit_trail);
|
||||
label += string_printf("|<f%d> bit skip %d", field++, int(knode.bit_skip));
|
||||
|
||||
if (is_inner) {
|
||||
label += string_printf("| <left> left");
|
||||
label += string_printf("| <right> right");
|
||||
}
|
||||
|
||||
const string knode_id = get_knode_id(knode);
|
||||
|
||||
/* Print node to the file. */
|
||||
fprintf(file, "\"%s\" [\n", knode_id.c_str());
|
||||
fprintf(file, " label = \"%s\"\n", label.c_str());
|
||||
fprintf(file, " shape = \"record\"\n");
|
||||
fprintf(file, "];\n");
|
||||
|
||||
/* Print relations. */
|
||||
if (is_inner) {
|
||||
recursive_print_knode(file, knode.inner.left_child, knodes, relation_id);
|
||||
recursive_print_knode(file, knode.inner.right_child, knodes, relation_id);
|
||||
|
||||
fprintf(file,
|
||||
"\"%s\":left -> \"%s\":f0 [ id = %d ];\n",
|
||||
knode_id.c_str(),
|
||||
get_knode_id(knodes[knode.inner.left_child]).c_str(),
|
||||
relation_id++);
|
||||
fprintf(file,
|
||||
"\"%s\":right -> \"%s\":f0 [ id = %d ];\n",
|
||||
knode_id.c_str(),
|
||||
get_knode_id(knodes[knode.inner.right_child]).c_str(),
|
||||
relation_id++);
|
||||
}
|
||||
}
|
||||
|
||||
void klight_tree_plot_to_file(uint root, const KernelLightTreeNode *knodes, const string &filename)
|
||||
{
|
||||
FILE *file = path_fopen(filename, "w");
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
int relation_id = 0;
|
||||
|
||||
fprintf(file, "digraph g {\n");
|
||||
fprintf(file, "graph [\n");
|
||||
fprintf(file, " rankdir = \"LR\"\n");
|
||||
fprintf(file, "];\n");
|
||||
recursive_print_knode(file, root, knodes, relation_id);
|
||||
fprintf(file, "}\n");
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
25
intern/cycles/scene/light_tree_debug.h
Normal file
25
intern/cycles/scene/light_tree_debug.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct KernelLightTreeNode;
|
||||
class LightTree;
|
||||
struct LightTreeNode;
|
||||
class Scene;
|
||||
|
||||
void light_tree_plot_to_file(const Scene &scene,
|
||||
const LightTree &tree,
|
||||
const LightTreeNode &root_node,
|
||||
const string &filename);
|
||||
|
||||
void klight_tree_plot_to_file(uint root_index,
|
||||
const KernelLightTreeNode *knodes,
|
||||
const string &filename);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user