This implements the initial core framework for fields and anonymous attributes (also see T91274). The new functionality is hidden behind the "Geometry Nodes Fields" feature flag. When enabled in the user preferences, the following new nodes become available: `Position`, `Index`, `Normal`, `Set Position` and `Attribute Capture`. Socket inspection has not been updated to work with fields yet. Besides these changes at the user level, this patch contains the ground work for: * building and evaluating fields at run-time (`FN_fields.hh`) and * creating and accessing anonymous attributes on geometry (`BKE_anonymous_attribute.h`). For evaluating fields we use a new so called multi-function procedure (`FN_multi_function_procedure.hh`). It allows composing multi-functions in arbitrary ways and supports efficient evaluation as is required by fields. See `FN_multi_function_procedure.hh` for more details on how this evaluation mechanism can be used. A new `AttributeIDRef` has been added which allows handling named and anonymous attributes in the same way in many places. Hans and I worked on this patch together. Differential Revision: https://developer.blender.org/D12414
176 lines
5.7 KiB
C++
176 lines
5.7 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "FN_multi_function_procedure_builder.hh"
|
|
|
|
namespace blender::fn {
|
|
|
|
void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction)
|
|
{
|
|
if (instruction_ == nullptr) {
|
|
if (is_entry_) {
|
|
procedure.set_entry(*new_instruction);
|
|
}
|
|
else {
|
|
/* The cursors points at nothing, nothing to do. */
|
|
}
|
|
}
|
|
else {
|
|
switch (instruction_->type()) {
|
|
case MFInstructionType::Call: {
|
|
static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
|
|
break;
|
|
}
|
|
case MFInstructionType::Branch: {
|
|
MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(
|
|
instruction_);
|
|
if (branch_output_) {
|
|
branch_instruction.set_branch_true(new_instruction);
|
|
}
|
|
else {
|
|
branch_instruction.set_branch_false(new_instruction);
|
|
}
|
|
break;
|
|
}
|
|
case MFInstructionType::Destruct: {
|
|
static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
|
|
break;
|
|
}
|
|
case MFInstructionType::Dummy: {
|
|
static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
|
|
break;
|
|
}
|
|
case MFInstructionType::Return: {
|
|
/* It shouldn't be possible to build a cursor that points to a return instruction. */
|
|
BLI_assert_unreachable();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MFProcedureBuilder::add_destruct(MFVariable &variable)
|
|
{
|
|
MFDestructInstruction &instruction = procedure_->new_destruct_instruction();
|
|
instruction.set_variable(&variable);
|
|
this->link_to_cursors(&instruction);
|
|
cursors_ = {MFInstructionCursor{instruction}};
|
|
}
|
|
|
|
void MFProcedureBuilder::add_destruct(Span<MFVariable *> variables)
|
|
{
|
|
for (MFVariable *variable : variables) {
|
|
this->add_destruct(*variable);
|
|
}
|
|
}
|
|
|
|
MFReturnInstruction &MFProcedureBuilder::add_return()
|
|
{
|
|
MFReturnInstruction &instruction = procedure_->new_return_instruction();
|
|
this->link_to_cursors(&instruction);
|
|
cursors_ = {};
|
|
return instruction;
|
|
}
|
|
|
|
MFCallInstruction &MFProcedureBuilder::add_call_with_no_variables(const MultiFunction &fn)
|
|
{
|
|
MFCallInstruction &instruction = procedure_->new_call_instruction(fn);
|
|
this->link_to_cursors(&instruction);
|
|
cursors_ = {MFInstructionCursor{instruction}};
|
|
return instruction;
|
|
}
|
|
|
|
MFCallInstruction &MFProcedureBuilder::add_call_with_all_variables(
|
|
const MultiFunction &fn, Span<MFVariable *> param_variables)
|
|
{
|
|
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
|
|
instruction.set_params(param_variables);
|
|
return instruction;
|
|
}
|
|
|
|
Vector<MFVariable *> MFProcedureBuilder::add_call(const MultiFunction &fn,
|
|
Span<MFVariable *> input_and_mutable_variables)
|
|
{
|
|
Vector<MFVariable *> output_variables;
|
|
MFCallInstruction &instruction = this->add_call_with_no_variables(fn);
|
|
for (const int param_index : fn.param_indices()) {
|
|
const MFParamType param_type = fn.param_type(param_index);
|
|
switch (param_type.interface_type()) {
|
|
case MFParamType::Input:
|
|
case MFParamType::Mutable: {
|
|
MFVariable *variable = input_and_mutable_variables.first();
|
|
instruction.set_param_variable(param_index, variable);
|
|
input_and_mutable_variables = input_and_mutable_variables.drop_front(1);
|
|
break;
|
|
}
|
|
case MFParamType::Output: {
|
|
MFVariable &variable = procedure_->new_variable(param_type.data_type(),
|
|
fn.param_name(param_index));
|
|
instruction.set_param_variable(param_index, &variable);
|
|
output_variables.append(&variable);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* All passed in variables should have been dropped in the loop above. */
|
|
BLI_assert(input_and_mutable_variables.is_empty());
|
|
return output_variables;
|
|
}
|
|
|
|
MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition)
|
|
{
|
|
MFBranchInstruction &instruction = procedure_->new_branch_instruction();
|
|
instruction.set_condition(&condition);
|
|
this->link_to_cursors(&instruction);
|
|
/* Clear cursors because this builder ends here. */
|
|
cursors_.clear();
|
|
|
|
Branch branch{*procedure_, *procedure_};
|
|
branch.branch_true.set_cursor(MFInstructionCursor{instruction, true});
|
|
branch.branch_false.set_cursor(MFInstructionCursor{instruction, false});
|
|
return branch;
|
|
}
|
|
|
|
MFProcedureBuilder::Loop MFProcedureBuilder::add_loop()
|
|
{
|
|
MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction();
|
|
MFDummyInstruction &loop_end = procedure_->new_dummy_instruction();
|
|
this->link_to_cursors(&loop_begin);
|
|
cursors_ = {MFInstructionCursor{loop_begin}};
|
|
|
|
Loop loop;
|
|
loop.begin = &loop_begin;
|
|
loop.end = &loop_end;
|
|
|
|
return loop;
|
|
}
|
|
|
|
void MFProcedureBuilder::add_loop_continue(Loop &loop)
|
|
{
|
|
this->link_to_cursors(loop.begin);
|
|
/* Clear cursors because this builder ends here. */
|
|
cursors_.clear();
|
|
}
|
|
|
|
void MFProcedureBuilder::add_loop_break(Loop &loop)
|
|
{
|
|
this->link_to_cursors(loop.end);
|
|
/* Clear cursors because this builder ends here. */
|
|
cursors_.clear();
|
|
}
|
|
|
|
} // namespace blender::fn
|