Geometry Nodes: add simulation support #104924

Closed
Hans Goudey wants to merge 211 commits from geometry-nodes-simulation into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 48 additions and 38 deletions
Showing only changes of commit 6bd0606438 - Show all commits

View File

@ -246,7 +246,7 @@ class NewGeometryNodeTreeAssign(Operator):
return {'FINISHED'}
class SimulationZoneOperator():
class SimulationZoneOperator:
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
@ -273,7 +273,7 @@ class SimulationZoneOperator():
class SimulationZoneItemAddOperator(SimulationZoneOperator, Operator):
'''Add a state item to the simulation zone'''
"""Add a state item to the simulation zone"""
bl_idname = "node.simulation_zone_item_add"
bl_label = "Add State Item"
bl_options = {'REGISTER', 'UNDO'}
@ -295,7 +295,7 @@ class SimulationZoneItemAddOperator(SimulationZoneOperator, Operator):
class SimulationZoneItemRemoveOperator(SimulationZoneOperator, Operator):
'''Remove a state item from the simulation zone'''
"""Remove a state item from the simulation zone"""
bl_idname = "node.simulation_zone_item_remove"
bl_label = "Remove State Item"
bl_options = {'REGISTER', 'UNDO'}
@ -312,7 +312,7 @@ class SimulationZoneItemRemoveOperator(SimulationZoneOperator, Operator):
class SimulationZoneItemMoveOperator(SimulationZoneOperator, Operator):
'''Move a simulation state item up or down in the list'''
"""Move a simulation state item up or down in the list"""
bl_idname = "node.simulation_zone_item_move"
bl_label = "Move State Item"
bl_options = {'REGISTER', 'UNDO'}

View File

@ -1426,9 +1426,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
/** \name Geometry Nodes
* \{ */
#define GEO_NODE_SIMULATION_INPUT 2100
#define GEO_NODE_SIMULATION_OUTPUT 2101
#define GEO_NODE_TRIANGULATE 1000
#define GEO_NODE_TRANSFORM_GEOMETRY 1002
#define GEO_NODE_MESH_BOOLEAN 1003
@ -1587,6 +1584,9 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_MEAN_FILTER_SDF_VOLUME 1197
#define GEO_NODE_OFFSET_SDF_VOLUME 1198
#define GEO_NODE_INDEX_OF_NEAREST 1199
/* Function nodes use the range starting at 1200. */
#define GEO_NODE_SIMULATION_INPUT 2100
#define GEO_NODE_SIMULATION_OUTPUT 2101
/** \} */

View File

@ -4331,7 +4331,9 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
do_versions_rename_id(bmain, ID_BR, "Draw Weight", "Weight Draw");
/* Identifier generation for simulation node sockets changed.
* Update identifiers so links are not removed during validation. */
* Update identifiers so links are not removed during validation.
* This only affects files that have been created using simulation nodes before they were first
* officially released. */
if (!DNA_struct_elem_find(fd->filesdna, "NodeSimulationItem", "int", "identifier")) {
static auto set_socket_identifiers =
[](bNode *node, const bNode *output_node, int extra_outputs) {

View File

@ -1596,19 +1596,21 @@ typedef struct NodeGeometryUVUnwrap {
uint8_t method;
} NodeGeometryUVUnwrap;
/* TODO: Add RAII */
typedef struct NodeSimulationItem {
char *name;
/* #eNodeSocketDatatype. */
/* TODO: Use a different enum instead to support Byte colors, etc. */
/** #eNodeSocketDatatype. */
short socket_type;
/** #eAttrDomain. */
short attribute_domain;
/* Generates unique identifier for sockets. */
/**
* Generates unique identifier for sockets which stays the same even when the item order or
* names change.
*/
int identifier;
} NodeSimulationItem;
typedef struct NodeGeometrySimulationInput {
/** bNode.identifier of the corresponding output node. */
int32_t output_node_id;
} NodeGeometrySimulationInput;
@ -1616,7 +1618,7 @@ typedef struct NodeGeometrySimulationOutput {
NodeSimulationItem *items;
int items_num;
int active_index;
/* Number to give unique IDs to state items. */
/** Number to give unique IDs to state items. */
int next_identifier;
int _pad;

View File

@ -4134,7 +4134,7 @@ static void rna_SimulationStateItem_color_get(PointerRNA *ptr, float *values)
NodeSimulationItem *item = (NodeSimulationItem *)ptr->data;
const char *socket_type_idname = nodeStaticSocketType(item->socket_type, 0);
node_type_draw_color(socket_type_idname, values);
ED_node_type_draw_color(socket_type_idname, values);
}
static PointerRNA rna_NodeGeometrySimulationInput_paired_output_get(PointerRNA *ptr)
@ -4221,8 +4221,8 @@ static void rna_NodeGeometrySimulationOutput_items_move(
{
NodeGeometrySimulationOutput *sim = (NodeGeometrySimulationOutput *)node->storage;
if (from_index < 0 || from_index >= sim->items_num || to_index < 0 ||
to_index >= sim->items_num) {
if (from_index < 0 || from_index >= sim->items_num || to_index < 0 || to_index >= sim->items_num)
{
return;
}

View File

@ -47,10 +47,12 @@ struct GeoNodesModifierData {
/** Optional logger. */
geo_eval_log::GeoModifierLog *eval_log = nullptr;
/** Read-only simulation states around the current frame. */
const bke::sim::ModifierSimulationState *current_simulation_state = nullptr;
const bke::sim::ModifierSimulationState *prev_simulation_state = nullptr;
const bke::sim::ModifierSimulationState *next_simulation_state = nullptr;
float simulation_state_mix_factor = 0.0f;
/** Used when the evaluation should create a new simulation state. */
bke::sim::ModifierSimulationState *current_simulation_state_for_write = nullptr;
float simulation_time_delta = 0.0f;

View File

@ -22,8 +22,6 @@ struct bNodeTree;
extern "C" {
#endif
void node_type_draw_color(const char *idname, float *r_color);
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,

View File

@ -34,11 +34,11 @@ std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item
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)));
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
BLI_assert(NOD_geometry_simulation_output_item_socket_type_supported(socket_type));
std::unique_ptr<SocketDeclaration> decl;
switch (eNodeSocketDatatype(item.socket_type)) {
switch (socket_type) {
case SOCK_FLOAT:
decl = std::make_unique<decl::Float>();
decl->input_field_type = InputSocketFieldType::IsSupported;
@ -179,7 +179,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
switch (socket_type) {
case SOCK_GEOMETRY: {
if (const auto *geo_state_item =
dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(&state_item)) {
dynamic_cast<const bke::sim::GeometrySimulationStateItem *>(&state_item))
{
GeometrySet *geometry = new (r_output_value) GeometrySet(geo_state_item->geometry());
geometries.append(geometry);
}
@ -196,7 +197,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
const fn::ValueOrFieldCPPType &value_or_field_type =
*fn::ValueOrFieldCPPType::get_from_self(cpp_type);
if (const auto *primitive_state_item =
dynamic_cast<const bke::sim::PrimitiveSimulationStateItem *>(&state_item)) {
dynamic_cast<const bke::sim::PrimitiveSimulationStateItem *>(&state_item))
{
if (primitive_state_item->type() == value_or_field_type.value) {
value_or_field_type.construct_from_value(r_output_value,
primitive_state_item->value());
@ -206,7 +208,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
}
}
else if (const auto *attribute_state_item =
dynamic_cast<const bke::sim::AttributeSimulationStateItem *>(&state_item)) {
dynamic_cast<const bke::sim::AttributeSimulationStateItem *>(&state_item))
{
AnonymousAttributeIDPtr attribute_id = MEM_new<NodeAnonymousAttributeID>(
__func__,
self_object,
@ -226,7 +229,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
}
case SOCK_STRING: {
if (const auto *string_state_item =
dynamic_cast<const bke::sim::StringSimulationStateItem *>(&state_item)) {
dynamic_cast<const bke::sim::StringSimulationStateItem *>(&state_item))
{
new (r_output_value) ValueOrField<std::string>(string_state_item->value());
}
else {
@ -246,7 +250,8 @@ void simulation_state_to_values(const Span<NodeSimulationItem> node_simulation_i
for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH,
GEO_COMPONENT_TYPE_CURVE,
GEO_COMPONENT_TYPE_POINT_CLOUD,
GEO_COMPONENT_TYPE_INSTANCES}) {
GEO_COMPONENT_TYPE_INSTANCES})
{
if (!geometry->has(type)) {
continue;
}
@ -415,6 +420,7 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
modifier_data.current_simulation_state->get_zone_state(zone_id) :
nullptr;
if (eval_data.is_first_evaluation && current_zone_state != nullptr) {
/* Common case when data is cached already. */
this->output_cached_state(
params, *modifier_data.self_object, *user_data.compute_context, *current_zone_state);
return;
@ -426,6 +432,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
modifier_data.prev_simulation_state->get_zone_state(zone_id) :
nullptr;
if (prev_zone_state == nullptr) {
/* There is no previous simulation state and we also don't create a new one, so just output
* defaults. */
params.set_default_remaining_outputs();
return;
}
@ -434,10 +442,12 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
modifier_data.next_simulation_state->get_zone_state(zone_id) :
nullptr;
if (next_zone_state == nullptr) {
/* Output the last cached simulation state. */
this->output_cached_state(
params, *modifier_data.self_object, *user_data.compute_context, *prev_zone_state);
return;
}
/* A previous and next frame is cached already, but the current frame is not. */
this->output_mixed_cached_state(params,
*modifier_data.self_object,
*user_data.compute_context,
@ -512,7 +522,8 @@ bke::sim::SimulationZoneID get_simulation_zone_id(const ComputeContext &compute_
{
bke::sim::SimulationZoneID zone_id;
for (const ComputeContext *context = &compute_context; context != nullptr;
context = context->parent()) {
context = context->parent())
{
if (const auto *node_context = dynamic_cast<const bke::NodeGroupComputeContext *>(context)) {
zone_id.node_ids.append(node_context->node_id());
}
@ -622,7 +633,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
if (link->tonode == node) {
if (link->tosock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
&storage, link->fromnode, link->fromsock)) {
&storage, link->fromnode, link->fromsock))
{
update_node_declaration_and_sockets(*ntree, *node);
link->tosock = nodeFindSocket(
node, SOCK_IN, socket_identifier_for_simulation_item(*item).c_str());
@ -636,7 +648,8 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
BLI_assert(link->fromnode == node);
if (link->fromsock->identifier == StringRef("__extend__")) {
if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket(
&storage, link->fromnode, link->tosock)) {
&storage, link->fromnode, link->tosock))
{
update_node_declaration_and_sockets(*ntree, *node);
link->fromsock = nodeFindSocket(
node, SOCK_OUT, socket_identifier_for_simulation_item(*item).c_str());
@ -777,8 +790,8 @@ 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))) {
if (!NOD_geometry_simulation_output_item_socket_type_supported(eNodeSocketDatatype(socket_type)))
{
return nullptr;
}

View File

@ -39,13 +39,6 @@ using namespace blender;
using blender::fn::ValueOrField;
using blender::nodes::SocketDeclarationPtr;
extern "C" void ED_node_type_draw_color(const char *idname, float *r_color);
void node_type_draw_color(const char *idname, float *r_color)
{
ED_node_type_draw_color(idname, r_color);
}
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,