Add support for attributes storage in simulation state #107133

Merged
4 changed files with 385 additions and 150 deletions

View File

@ -256,6 +256,103 @@ static OutputFieldDependency find_group_output_dependencies(
return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices));
}
/** Result of syncing two field states. */
enum class eFieldStateSyncResult : char {

enum class FieldStateSyncResult

`enum class FieldStateSyncResult`
/* Nothing changed. */
NONE = 0,
/* State A has been modified. */
CHANGED_A = (1 << 0),
/* State B has been modified. */
CHANGED_B = (1 << 1),
};
ENUM_OPERATORS(eFieldStateSyncResult, eFieldStateSyncResult::CHANGED_B)
/**

Blender's doxygen syntax uses \return rather than @return

Blender's doxygen syntax uses `\return` rather than `@return`
* Compare both field states and select the most compatible.
* Afterwards both field states will be the same.
LukasTonne marked this conversation as resolved
Review

Feels like it would be simpler to just change the values and then compare them to a copy afterwards to detect what has changed.

Feels like it would be simpler to just change the values and then compare them to a copy afterwards to detect what has changed.
Review

You're right, combining states first and then comparing is a lot shorter.

You're right, combining states first and then comparing is a lot shorter.
* \return eFieldStateSyncResult flags indicating which field states have changed.
*/
LukasTonne marked this conversation as resolved
Review

Rather than using int, you should be able to use the ENUM_OPERATORS macro to define the necessary operations for the enum type

Rather than using `int`, you should be able to use the `ENUM_OPERATORS` macro to define the necessary operations for the enum type
static eFieldStateSyncResult sync_field_states(SocketFieldState &a, SocketFieldState &b)
{
const bool requires_single = a.requires_single || b.requires_single;
const bool is_single = a.is_single && b.is_single;

Use functional style cast for enums: FieldStateSyncResult(0). Might be even better to simply add a None item to the enum.

https://wiki.blender.org/wiki/Style_Guide/C_Cpp#C.2B.2B_Type_Cast

Use functional style cast for enums: `FieldStateSyncResult(0)`. Might be even better to simply add a `None` item to the enum. https://wiki.blender.org/wiki/Style_Guide/C_Cpp#C.2B.2B_Type_Cast
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
if (a.requires_single != requires_single || a.is_single != is_single) {
res |= eFieldStateSyncResult::CHANGED_A;
}
if (b.requires_single != requires_single || b.is_single != is_single) {
res |= eFieldStateSyncResult::CHANGED_B;
}
a.requires_single = requires_single;
b.requires_single = requires_single;
a.is_single = is_single;
b.is_single = is_single;
return res;
}
/**
* Compare field states of simulation nodes sockets and select the most compatible.
* Afterwards all field states will be the same.
* \return eFieldStateSyncResult flags indicating which field states have changed.
*/
static eFieldStateSyncResult simulation_nodes_field_state_sync(
const bNode &input_node,
const bNode &output_node,
const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
eFieldStateSyncResult res = eFieldStateSyncResult::NONE;
for (const int i : output_node.output_sockets().index_range()) {
/* First input node output is Delta Time which does not appear in the output node outputs. */
const bNodeSocket &input_socket = input_node.output_socket(i + 1);
const bNodeSocket &output_socket = output_node.output_socket(i);
SocketFieldState &input_state = field_state_by_socket_id[input_socket.index_in_tree()];
SocketFieldState &output_state = field_state_by_socket_id[output_socket.index_in_tree()];
res |= sync_field_states(input_state, output_state);
}
return res;
}
static bool propagate_special_data_requirements(
const bNodeTree &tree,
const bNode &node,
const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
tree.ensure_topology_cache();
bool need_update = false;
/* Sync field state between simulation nodes and schedule another pass if necessary. */
if (node.type == GEO_NODE_SIMULATION_INPUT) {
const NodeGeometrySimulationInput &data = *static_cast<const NodeGeometrySimulationInput *>(
node.storage);

const, same below

`const`, same below
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
node, *output_node, field_state_by_socket_id);
if (bool(sync_result & eFieldStateSyncResult::CHANGED_B)) {
LukasTonne marked this conversation as resolved Outdated

Functional cast for bool: (bool)... -> bool(...)

Though I often see implicit conversions used for cases like this, which seems fine IMO:
if (sync_result & eFieldStateSyncResult::CHANGED_B) {

Functional cast for `bool`: `(bool)...` -> `bool(...)` Though I often see implicit conversions used for cases like this, which seems fine IMO: `if (sync_result & eFieldStateSyncResult::CHANGED_B) {`

Implicit cast to bool does not work with enum class unfortunately (suggested by Jacques above). Functional cast should be fine though.

Implicit cast to bool does not work with `enum class` unfortunately (suggested by Jacques above). Functional cast should be fine though.
need_update = true;
}
}
}
else if (node.type == GEO_NODE_SIMULATION_OUTPUT) {
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
LukasTonne marked this conversation as resolved
Review

Can use input_socket(index) instead of input_sockets()[index]

Can use `input_socket(index)` instead of `input_sockets()[index]`
const NodeGeometrySimulationInput &data = *static_cast<const NodeGeometrySimulationInput *>(
input_node->storage);
if (node.identifier == data.output_node_id) {
const eFieldStateSyncResult sync_result = simulation_nodes_field_state_sync(
*input_node, node, field_state_by_socket_id);
if (bool(sync_result & eFieldStateSyncResult::CHANGED_A)) {
need_update = true;
}
}
}
}
return need_update;
}
static void propagate_data_requirements_from_right_to_left(
const bNodeTree &tree,
const Span<const FieldInferencingInterface *> interface_by_node,
@ -263,70 +360,85 @@ static void propagate_data_requirements_from_right_to_left(
{
const Span<const bNode *> toposort_result = tree.toposort_right_to_left();

[...] caused by simulation input/output nodes.

`[...] caused by simulation input/output nodes.`
for (const bNode *node : toposort_result) {
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
while (true) {
/* Node updates may require sevaral passes due to cyclic dependencies caused by simulation
* input/output nodes. */
bool need_update = false;
for (const bNodeSocket *output_socket : node->output_sockets()) {
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
for (const bNode *node : toposort_result) {
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
for (const bNodeSocket *output_socket : node->output_sockets()) {
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) {
continue;
}
if (field_dependency.field_type() == OutputSocketFieldType::None) {
state.requires_single = true;
state.is_always_single = true;
continue;
}
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
/* The output is required to be a single value when it is connected to any input that does
* not support fields. */
for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) {
if (target_socket->is_available()) {
state.requires_single |=
field_state_by_socket_id[target_socket->index_in_tree()].requires_single;
if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) {
continue;
}
if (field_dependency.field_type() == OutputSocketFieldType::None) {
state.requires_single = true;
state.is_always_single = true;
continue;
}
}
if (state.requires_single) {
bool any_input_is_field_implicitly = false;
const Vector<const bNodeSocket *> connected_inputs = gather_input_socket_dependencies(
field_dependency, *node);
for (const bNodeSocket *input_socket : connected_inputs) {
if (!input_socket->is_available()) {
continue;
/* The output is required to be a single value when it is connected to any input that does
* not support fields. */
for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) {
if (target_socket->is_available()) {
state.requires_single |=
field_state_by_socket_id[target_socket->index_in_tree()].requires_single;
}
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
if (!input_socket->is_logically_linked()) {
any_input_is_field_implicitly = true;
break;
}
if (state.requires_single) {
bool any_input_is_field_implicitly = false;
const Vector<const bNodeSocket *> connected_inputs = gather_input_socket_dependencies(
field_dependency, *node);
for (const bNodeSocket *input_socket : connected_inputs) {
if (!input_socket->is_available()) {
continue;
}
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
if (!input_socket->is_logically_linked()) {
any_input_is_field_implicitly = true;
break;
}
}
}
if (any_input_is_field_implicitly) {
/* This output isn't a single value actually. */
state.requires_single = false;
}
else {
/* If the output is required to be a single value, the connected inputs in the same
* node must not be fields as well. */
for (const bNodeSocket *input_socket : connected_inputs) {
field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true;
}
}
}
if (any_input_is_field_implicitly) {
/* This output isn't a single value actually. */
state.requires_single = false;
}
else {
/* If the output is required to be a single value, the connected inputs in the same node
* must not be fields as well. */
for (const bNodeSocket *input_socket : connected_inputs) {
field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true;
}
}
/* Some inputs do not require fields independent of what the outputs are connected to. */
for (const bNodeSocket *input_socket : node->input_sockets()) {
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
state.requires_single = true;
state.is_always_single = true;
}
}
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
need_update = true;
}
}
/* Some inputs do not require fields independent of what the outputs are connected to. */
for (const bNodeSocket *input_socket : node->input_sockets()) {
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
state.requires_single = true;
state.is_always_single = true;
}
if (!need_update) {
break;
}
}
}
@ -382,68 +494,82 @@ static void propagate_field_status_from_left_to_right(
{
const Span<const bNode *> toposort_result = tree.toposort_left_to_right();
for (const bNode *node : toposort_result) {
if (node->type == NODE_GROUP_INPUT) {
continue;
}
while (true) {
/* Node updates may require sevaral passes due to cyclic dependencies. */
bool need_update = false;
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
/* Update field state of input sockets, also taking into account linked origin sockets. */
for (const bNodeSocket *input_socket : node->input_sockets()) {
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (state.is_always_single) {
state.is_single = true;
for (const bNode *node : toposort_result) {
if (node->type == NODE_GROUP_INPUT) {
continue;
}
state.is_single = true;
if (!input_socket->is_directly_linked()) {
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
state.is_single = false;
const FieldInferencingInterface &inferencing_interface = *interface_by_node[node->index()];
/* Update field state of input sockets, also taking into account linked origin sockets. */
for (const bNodeSocket *input_socket : node->input_sockets()) {
SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (state.is_always_single) {
state.is_single = true;
continue;
}
}
else {
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) {
state.is_single = true;
if (!input_socket->is_directly_linked()) {
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
state.is_single = false;
break;
}
}
}
}
/* Update field state of output sockets, also taking into account input sockets. */
for (const bNodeSocket *output_socket : node->output_sockets()) {
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
switch (field_dependency.field_type()) {
case OutputSocketFieldType::None: {
state.is_single = true;
break;
}
case OutputSocketFieldType::FieldSource: {
state.is_single = false;
state.is_field_source = true;
break;
}
case OutputSocketFieldType::PartiallyDependent:
case OutputSocketFieldType::DependentField: {
for (const bNodeSocket *input_socket :
gather_input_socket_dependencies(field_dependency, *node)) {
if (!input_socket->is_available()) {
continue;
}
if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
else {
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) {
state.is_single = false;
break;
}
}
break;
}
}
/* Update field state of output sockets, also taking into account input sockets. */
for (const bNodeSocket *output_socket : node->output_sockets()) {
SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
switch (field_dependency.field_type()) {
case OutputSocketFieldType::None: {
state.is_single = true;
break;
}
case OutputSocketFieldType::FieldSource: {
state.is_single = false;
state.is_field_source = true;
break;
}
case OutputSocketFieldType::PartiallyDependent:
case OutputSocketFieldType::DependentField: {
for (const bNodeSocket *input_socket :
gather_input_socket_dependencies(field_dependency, *node)) {
if (!input_socket->is_available()) {
continue;
}
if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
state.is_single = false;
break;
}
}
break;
}
}
}
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
if (propagate_special_data_requirements(tree, *node, field_state_by_socket_id)) {
need_update = true;
}
}
if (!need_update) {
break;
}
}
}

View File

@ -134,8 +134,14 @@ class FieldAtIndexInput final : public bke::GeometryFieldInput {
};
std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item);
void socket_declarations_for_simulation_items(Span<NodeSimulationItem> items,
NodeDeclaration &r_declaration);
const CPPType &get_simulation_item_cpp_type(eNodeSocketDatatype socket_type);
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item);
void copy_simulation_state_to_output_param(lf::Params &params,
int index,
eNodeSocketDatatype socket_type,
const bke::sim::SimulationStateItem &state_item);
} // namespace blender::nodes

View File

@ -13,6 +13,26 @@
#include "node_geometry_util.hh"
namespace blender::nodes {
/** Copy a node input parameter to the matching output parameter. */
static void copy_input_to_output_param(lf::Params &params,
const int index,
const eNodeSocketDatatype socket_type)
{

Might be worth adding a comment explaining the + 1 here, so the reader doesn't have to look elsewhere to figure it out.

Might be worth adding a comment explaining the `+ 1` here, so the reader doesn't have to look elsewhere to figure it out.
const CPPType &cpptype = get_simulation_item_cpp_type(socket_type);
void *src = params.try_get_input_data_ptr_or_request(index);
if (src != nullptr) {
/* First output parameter is "Delta Time", state item parameters start at index 1. */
void *dst = params.get_output_data_ptr(index + 1);
cpptype.move_construct(src, dst);
params.output_set(index + 1);
}
}
LukasTonne marked this conversation as resolved
Review

Maybe I'm being stupid, but I don't get what's meant by "next" here.

Maybe I'm being stupid, but I don't get what's meant by "next" here.
Review

Was just an ad-hoc name: it's copying from the simulation state into the output parameter for the next iteration. I'll add some comments.

Was just an ad-hoc name: it's copying from the simulation state into the output parameter for the next iteration. I'll add some comments.
Review

Renamed the functions and added comments for clarity. Also the copy_simulation_state_to_output_param function is now shared by input and output node, they both need to do this: input node when preparing the next iteration, output node when outputting the sim result.

Renamed the functions and added comments for clarity. Also the `copy_simulation_state_to_output_param` function is now shared by input and output node, they both need to do this: input node when preparing the next iteration, output node when outputting the sim result.
} // namespace blender::nodes
namespace blender::nodes::node_geo_simulation_input_cc {
NODE_STORAGE_FUNCS(NodeGeometrySimulationInput);
@ -65,10 +85,8 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
if (params.output_was_set(i + 1)) {
continue;
}
LukasTonne marked this conversation as resolved Outdated

int index -> const int index

`int index` -> `const int index`
GeometrySet *geometry = params.try_get_input_data_ptr_or_request<GeometrySet>(i);
if (geometry != nullptr) {
params.set_output(i + 1, std::move(*geometry));
}
copy_input_to_output_param(

use eNodeSocketDatatype instead of short

use `eNodeSocketDatatype` instead of `short`
params, i, eNodeSocketDatatype(simulation_items_[i].socket_type));
}
}
else {
@ -77,10 +95,11 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
params.set_output(i + 1, GeometrySet());
continue;
}
const bke::sim::SimulationStateItem *item = prev_zone_state->items[i].get();
if (auto *geometry_item = dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(
item)) {
params.set_output(i + 1, geometry_item->geometry());
const bke::sim::SimulationStateItem *state_item = prev_zone_state->items[i].get();
if (state_item != nullptr) {
/* First output parameter is "Delta Time", state item parameters start at index 1. */
copy_simulation_state_to_output_param(
params, i + 1, eNodeSocketDatatype(simulation_items_[i].socket_type), *state_item);
}
}
}

View File

@ -21,31 +21,62 @@ std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item
return "Item_" + std::to_string(item.identifier);
}
static std::unique_ptr<SocketDeclaration> socket_declaration_for_simulation_item(
const NodeSimulationItem &item, const eNodeSocketInOut in_out, const int index)
{
BLI_assert(NOD_geometry_simulation_output_item_socket_type_supported(
eNodeSocketDatatype(item.socket_type)));
std::unique_ptr<SocketDeclaration> decl;
switch (eNodeSocketDatatype(item.socket_type)) {
case SOCK_FLOAT:
decl = std::make_unique<decl::Float>();
decl->input_field_type = InputSocketFieldType::IsSupported;
decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index});
break;
case SOCK_VECTOR:
decl = std::make_unique<decl::Vector>();
decl->input_field_type = InputSocketFieldType::IsSupported;
decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index});
break;
case SOCK_RGBA:
decl = std::make_unique<decl::Color>();
decl->input_field_type = InputSocketFieldType::IsSupported;
decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index});
break;
case SOCK_BOOLEAN:
decl = std::make_unique<decl::Bool>();
decl->input_field_type = InputSocketFieldType::IsSupported;
decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index});
break;
case SOCK_INT:
decl = std::make_unique<decl::Int>();
decl->input_field_type = InputSocketFieldType::IsSupported;
decl->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField({index});
break;
case SOCK_STRING:
decl = std::make_unique<decl::String>();
break;
case SOCK_GEOMETRY:
decl = std::make_unique<decl::Geometry>();
break;
default:
BLI_assert_unreachable();
}
decl->name = item.name ? item.name : "";
decl->identifier = socket_identifier_for_simulation_item(item);
decl->in_out = in_out;
return decl;
}
void socket_declarations_for_simulation_items(const Span<NodeSimulationItem> items,
NodeDeclaration &r_declaration)
{
for (const NodeSimulationItem &item : items) {
switch (eNodeSocketDatatype(item.socket_type)) {
case SOCK_GEOMETRY: {
{
std::unique_ptr<decl::Geometry> decl = std::make_unique<decl::Geometry>();
decl->name = item.name ? item.name : "";
decl->identifier = socket_identifier_for_simulation_item(item);
decl->in_out = SOCK_IN;
r_declaration.inputs.append(std::move(decl));
}
{
std::unique_ptr<decl::Geometry> decl = std::make_unique<decl::Geometry>();
decl->name = item.name ? item.name : "";
decl->identifier = socket_identifier_for_simulation_item(item);
decl->in_out = SOCK_OUT;
r_declaration.outputs.append(std::move(decl));
}
break;
}
default:
BLI_assert_unreachable();
}
for (const int i : items.index_range()) {
const NodeSimulationItem &item = items[i];
r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN, i));
r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT, i));
}
r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN));
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
@ -73,14 +104,56 @@ static bool simulation_items_unique_name_check(void *arg, const char *name)
return false;
}
const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_type)
{
const char *socket_idname = nodeStaticSocketType(socket_type, 0);
const bNodeSocketType *typeinfo = nodeSocketTypeFind(socket_idname);
BLI_assert(typeinfo);
LukasTonne marked this conversation as resolved Outdated

functional style cast, few more cases below

functional style cast, few more cases below
BLI_assert(typeinfo->geometry_nodes_cpp_type);
return *typeinfo->geometry_nodes_cpp_type;
}
const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item)
{
switch (item.socket_type) {
case SOCK_GEOMETRY:
return CPPType::get<GeometrySet>();
default:
BLI_assert_unreachable();
return CPPType::get<GeometrySet>();
return get_simulation_item_cpp_type(eNodeSocketDatatype(item.socket_type));
}
/** Create a simulation state item from parameter input data. */
static std::unique_ptr<bke::sim::SimulationStateItem> make_simulation_state_item(
const eNodeSocketDatatype socket_type, void *input_data)
{
if (socket_type == SOCK_GEOMETRY) {
GeometrySet *input_geometry = static_cast<GeometrySet *>(input_data);
input_geometry->ensure_owns_direct_data();
return std::make_unique<bke::sim::GeometrySimulationStateItem>(*input_geometry);
}
else {
/* TODO: Implement for non-geometry state items. */
GeometrySet geometry;
return std::make_unique<bke::sim::GeometrySimulationStateItem>(geometry);
}
}
/** Copy the current simulation state to the node output parameter. */
void copy_simulation_state_to_output_param(lf::Params &params,
const int index,

I'd hope this could use the socket type's existing geometry_nodes_cpp_type callback.

I'd hope this could use the socket type's existing `geometry_nodes_cpp_type` callback.

Yeah that works too. I just wanted to keep it simple, considering we have to do a bunch of type switches elsewhere anyway. Have to get the type idname from the eNodeSocketDatatype, then get the bNodeSocketType from the idname, then get the CPPType.

Yeah that works too. I just wanted to keep it simple, considering we have to do a bunch of type switches elsewhere anyway. Have to get the type `idname` from the `eNodeSocketDatatype`, then get the `bNodeSocketType` from the `idname`, then get the CPPType.
const eNodeSocketDatatype socket_type,
const bke::sim::SimulationStateItem &state_item)
{
const CPPType &cpptype = get_simulation_item_cpp_type(socket_type);
if (socket_type == SOCK_GEOMETRY) {
const bke::sim::GeometrySimulationStateItem &geo_state_item =
static_cast<const bke::sim::GeometrySimulationStateItem &>(state_item);
params.set_output(index, geo_state_item.geometry());
}
else {
/* TODO: Implement for non-geometry state items. */
if (const void *src = cpptype.default_value()) {
void *dst = params.get_output_data_ptr(index);
cpptype.copy_construct(src, dst);
params.output_set(index);
}
}
}
@ -168,14 +241,16 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
bool all_available = true;
for (const int i : simulation_items_.index_range()) {
GeometrySet *input_geometry = params.try_get_input_data_ptr_or_request<GeometrySet>(i);
if (input_geometry == nullptr) {
const NodeSimulationItem &item = simulation_items_[i];
void *input_data = params.try_get_input_data_ptr_or_request(i);
if (input_data == nullptr) {
all_available = false;
continue;
}
input_geometry->ensure_owns_direct_data();
new_zone_state.items[i] = std::make_unique<bke::sim::GeometrySimulationStateItem>(
std::move(*input_geometry));
new_zone_state.items[i] = make_simulation_state_item(eNodeSocketDatatype(item.socket_type),
input_data);
}
if (all_available) {
@ -186,17 +261,17 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
void output_cached_state(lf::Params &params, const bke::sim::SimulationZoneState &state) const
{
for (const int i : simulation_items_.index_range()) {
const NodeSimulationItem &item = simulation_items_[i];
if (i >= state.items.size()) {
continue;
}
const bke::sim::SimulationStateItem *item = state.items[i].get();
if (item == nullptr) {
const bke::sim::SimulationStateItem *state_item = state.items[i].get();
if (state_item == nullptr) {
continue;
}
if (auto *geometry_item = dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(
item)) {
params.set_output(i, geometry_item->geometry());
}
copy_simulation_state_to_output_param(
params, i, eNodeSocketDatatype(item.socket_type), *state_item);
}
params.set_default_remaining_outputs();
}
@ -369,7 +444,14 @@ blender::IndexRange NodeGeometrySimulationOutput::items_range() const
bool NOD_geometry_simulation_output_item_socket_type_supported(
const eNodeSocketDatatype socket_type)
{
return ELEM(socket_type, SOCK_GEOMETRY);
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY);
}
bNode *NOD_geometry_simulation_output_find_node_by_item(bNodeTree *ntree,
@ -450,6 +532,11 @@ NodeSimulationItem *NOD_geometry_simulation_output_insert_item(NodeGeometrySimul
const char *name,
int index)
{
if (!NOD_geometry_simulation_output_item_socket_type_supported(
eNodeSocketDatatype(socket_type))) {
return nullptr;
}
NodeSimulationItem *old_items = sim->items;
sim->items = MEM_cnew_array<NodeSimulationItem>(sim->items_num + 1, __func__);
for (const int i : blender::IndexRange(index)) {
@ -474,9 +561,6 @@ NodeSimulationItem *NOD_geometry_simulation_output_insert_item(NodeGeometrySimul
NodeSimulationItem *NOD_geometry_simulation_output_add_item_from_socket(
NodeGeometrySimulationOutput *sim, const bNode * /*from_node*/, const bNodeSocket *from_sock)
{
if (from_sock->type != SOCK_GEOMETRY) {
return nullptr;
}
return NOD_geometry_simulation_output_insert_item(
sim, from_sock->type, from_sock->name, sim->items_num);
}