Add support for attributes storage in simulation state #107133
|
@ -13,21 +13,89 @@ class SimulationStateItem {
|
|||
virtual ~SimulationStateItem() = default;
|
||||
};
|
||||
|
||||
class GeometrySimulationStateItem : public SimulationStateItem {
|
||||
template <typename T>
|
||||
class TypedSimulationStateItem : public SimulationStateItem {
|
||||
|
||||
public:
|
||||
using DataType = T;
|
||||
|
||||
private:
|
||||
GeometrySet geometry_;
|
||||
T data_;
|
||||
|
||||
public:
|
||||
GeometrySimulationStateItem(GeometrySet geometry) : geometry_(std::move(geometry))
|
||||
TypedSimulationStateItem() = default;
|
||||
TypedSimulationStateItem(const T &data)
|
||||
Jacques Lucke
commented
Most of these methods/constructors seem unnecessary. A simple constructor that takes a Most of these methods/constructors seem unnecessary. A simple constructor that takes a `T value` that is then moved into `data_` should be good enough. (Calling `std::move` on a const reference as is currently done here does not make sense anyway)
Lukas Tönne
commented
TBH i just added a bunch of common constructors without considering the details, just to get it working. TBH i just added a bunch of common constructors without considering the details, just to get it working.
|
||||
{
|
||||
data_ = std::move(data);
|
||||
}
|
||||
TypedSimulationStateItem(T &&data)
|
||||
{
|
||||
data_ = std::move(data);
|
||||
}
|
||||
template <typename U>
|
||||
TypedSimulationStateItem(const U &data)
|
||||
{
|
||||
data_ = data;
|
||||
}
|
||||
Jacques Lucke
commented
If this is really need (and I'm not sure why it is right now), consider adding If this is really need (and I'm not sure why it is right now), consider adding `NonCopyable` and `NonMovable` as base class.
|
||||
|
||||
const GeometrySet &geometry() const
|
||||
TypedSimulationStateItem(const TypedSimulationStateItem &) = delete;
|
||||
TypedSimulationStateItem(TypedSimulationStateItem &&) = delete;
|
||||
|
||||
virtual ~TypedSimulationStateItem() {
|
||||
}
|
||||
|
||||
TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete;
|
||||
TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete;
|
||||
|
||||
const T &data() const
|
||||
{
|
||||
return geometry_;
|
||||
return data_;
|
||||
}
|
||||
};
|
||||
|
||||
/** Specialization for GeometrySet which ensures the state owns the geometry data. */
|
||||
template <>
|
||||
class TypedSimulationStateItem<GeometrySet> : public SimulationStateItem {
|
||||
public:
|
||||
using DataType = GeometrySet;
|
||||
|
||||
private:
|
||||
GeometrySet data_;
|
||||
|
||||
public:
|
||||
TypedSimulationStateItem() = default;
|
||||
TypedSimulationStateItem(const GeometrySet &data)
|
||||
{
|
||||
data_ = std::move(data);
|
||||
data_.ensure_owns_direct_data();
|
||||
}
|
||||
TypedSimulationStateItem(GeometrySet &&data)
|
||||
{
|
||||
data_ = std::move(data);
|
||||
data_.ensure_owns_direct_data();
|
||||
}
|
||||
template <typename U>
|
||||
TypedSimulationStateItem(const U &data)
|
||||
{
|
||||
data_ = data;
|
||||
data_.ensure_owns_direct_data();
|
||||
}
|
||||
|
||||
TypedSimulationStateItem(const TypedSimulationStateItem &) = delete;
|
||||
TypedSimulationStateItem(TypedSimulationStateItem &&) = delete;
|
||||
|
||||
virtual ~TypedSimulationStateItem() {
|
||||
}
|
||||
|
||||
TypedSimulationStateItem &operator=(const TypedSimulationStateItem &) = delete;
|
||||
TypedSimulationStateItem &operator=(TypedSimulationStateItem &&) = delete;
|
||||
|
||||
const GeometrySet &data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SimulationZoneState {
|
||||
public:
|
||||
Vector<std::unique_ptr<SimulationStateItem>> items;
|
||||
|
|
|
@ -12,6 +12,128 @@
|
|||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
template <typename T>
|
||||
static void copy_typed_initial_simulation_state(lf::Params ¶ms, int index)
|
||||
{
|
||||
T *data = params.try_get_input_data_ptr_or_request<T>(index);
|
||||
if (data != nullptr) {
|
||||
params.set_output(index + 1, std::move(*data));
|
||||
Hans Goudey
commented
Might be worth adding a comment explaining the Might be worth adding a comment explaining the `+ 1` here, so the reader doesn't have to look elsewhere to figure it out.
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_initial_simulation_state(
|
||||
lf::Params ¶ms,
|
||||
int index,
|
||||
short socket_type)
|
||||
{
|
||||
switch (socket_type) {
|
||||
case SOCK_FLOAT:
|
||||
LukasTonne marked this conversation as resolved
Hans Goudey
commented
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.
Lukas Tönne
commented
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.
Lukas Tönne
commented
Renamed the functions and added comments for clarity. Also the 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.
|
||||
copy_typed_initial_simulation_state<ValueOrField<float>>(params, index);
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
copy_typed_initial_simulation_state<ValueOrField<float3>>(params, index);
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
copy_typed_initial_simulation_state<ValueOrField<ColorGeometry4f>>(params, index);
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Might as well include what has to be done in the TODO comment Might as well include what has to be done in the TODO comment
|
||||
break;
|
||||
case SOCK_BOOLEAN:
|
||||
copy_typed_initial_simulation_state<ValueOrField<bool>>(params, index);
|
||||
break;
|
||||
case SOCK_INT:
|
||||
copy_typed_initial_simulation_state<ValueOrField<int>>(params, index);
|
||||
break;
|
||||
case SOCK_STRING:
|
||||
LukasTonne marked this conversation as resolved
Outdated
Jacques Lucke
commented
remove dead code remove dead code
|
||||
copy_typed_initial_simulation_state<ValueOrField<std::string>>(params, index);
|
||||
break;
|
||||
case SOCK_OBJECT:
|
||||
copy_typed_initial_simulation_state<Object *>(params, index);
|
||||
break;
|
||||
case SOCK_GEOMETRY:
|
||||
copy_typed_initial_simulation_state<GeometrySet>(params, index);
|
||||
break;
|
||||
case SOCK_COLLECTION:
|
||||
copy_typed_initial_simulation_state<Collection *>(params, index);
|
||||
break;
|
||||
case SOCK_TEXTURE:
|
||||
copy_typed_initial_simulation_state<Tex *>(params, index);
|
||||
break;
|
||||
case SOCK_IMAGE:
|
||||
copy_typed_initial_simulation_state<Image *>(params, index);
|
||||
break;
|
||||
case SOCK_MATERIAL:
|
||||
copy_typed_initial_simulation_state<Material *>(params, index);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
copy_typed_initial_simulation_state<GeometrySet>(params, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void copy_typed_next_simulation_state(lf::Params ¶ms,
|
||||
int index,
|
||||
const bke::sim::SimulationStateItem &state_item)
|
||||
{
|
||||
if (auto *typed_state_item = dynamic_cast<const bke::sim::TypedSimulationStateItem<T> *>(
|
||||
Hans Goudey
commented
`auto *` -> `const auto *`
|
||||
&state_item)) {
|
||||
params.set_output(index + 1, typed_state_item->data());
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_next_simulation_state(lf::Params ¶ms,
|
||||
int index,
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
`int index` -> `const int index`
|
||||
short socket_type,
|
||||
Hans Goudey
commented
use use `eNodeSocketDatatype` instead of `short`
|
||||
const bke::sim::SimulationStateItem &state_item)
|
||||
{
|
||||
switch (socket_type) {
|
||||
case SOCK_FLOAT:
|
||||
copy_typed_next_simulation_state<ValueOrField<float>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
copy_typed_next_simulation_state<ValueOrField<float3>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
copy_typed_next_simulation_state<ValueOrField<ColorGeometry4f>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_BOOLEAN:
|
||||
copy_typed_next_simulation_state<ValueOrField<bool>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_INT:
|
||||
copy_typed_next_simulation_state<ValueOrField<int>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_STRING:
|
||||
copy_typed_next_simulation_state<ValueOrField<std::string>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_OBJECT:
|
||||
copy_typed_next_simulation_state<Object *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_GEOMETRY:
|
||||
copy_typed_next_simulation_state<GeometrySet>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_COLLECTION:
|
||||
copy_typed_next_simulation_state<Collection *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_TEXTURE:
|
||||
copy_typed_next_simulation_state<Tex *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_IMAGE:
|
||||
copy_typed_next_simulation_state<Image *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_MATERIAL:
|
||||
Jacques Lucke
commented
functional style cast for enums functional style cast for enums
|
||||
copy_typed_next_simulation_state<Material *>(params, index, state_item);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
copy_typed_next_simulation_state<GeometrySet>(params, index, state_item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace blender::nodes::node_geo_simulation_input_cc {
|
||||
|
||||
NODE_STORAGE_FUNCS(NodeGeometrySimulationInput);
|
||||
|
@ -63,10 +185,7 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction {
|
|||
if (params.output_was_set(i + 1)) {
|
||||
continue;
|
||||
}
|
||||
GeometrySet *geometry = params.try_get_input_data_ptr_or_request<GeometrySet>(i);
|
||||
if (geometry != nullptr) {
|
||||
params.set_output(i + 1, std::move(*geometry));
|
||||
}
|
||||
copy_initial_simulation_state(params, i, simulation_items_[i].socket_type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -75,10 +194,9 @@ 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) {
|
||||
copy_next_simulation_state(params, i, simulation_items_[i].socket_type, *state_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,112 @@ const CPPType &get_simulation_item_cpp_type(const NodeSimulationItem &item)
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::unique_ptr<bke::sim::TypedSimulationStateItem<T>> make_typed_simulation_state_item(lf::Params ¶ms, int index)
|
||||
{
|
||||
using bke::sim::TypedSimulationStateItem;
|
||||
|
||||
if (const T *data = params.try_get_input_data_ptr_or_request<T>(index)) {
|
||||
return std::make_unique<TypedSimulationStateItem<T>>(*data);
|
||||
}
|
||||
|
||||
return std::make_unique<TypedSimulationStateItem<T>>();
|
||||
}
|
||||
|
||||
static std::unique_ptr<bke::sim::SimulationStateItem> make_simulation_state_item(
|
||||
lf::Params ¶ms, int index, short socket_type)
|
||||
{
|
||||
switch (socket_type) {
|
||||
case SOCK_FLOAT:
|
||||
return make_typed_simulation_state_item<ValueOrField<float>>(params, index);
|
||||
case SOCK_VECTOR:
|
||||
return make_typed_simulation_state_item<ValueOrField<float3>>(params, index);
|
||||
case SOCK_RGBA:
|
||||
return make_typed_simulation_state_item<ValueOrField<ColorGeometry4f>>(params, index);
|
||||
case SOCK_BOOLEAN:
|
||||
return make_typed_simulation_state_item<ValueOrField<bool>>(params, index);
|
||||
case SOCK_INT:
|
||||
return make_typed_simulation_state_item<ValueOrField<int>>(params, index);
|
||||
case SOCK_STRING:
|
||||
return make_typed_simulation_state_item<ValueOrField<std::string>>(params, index);
|
||||
case SOCK_OBJECT:
|
||||
return make_typed_simulation_state_item<Object *>(params, index);
|
||||
case SOCK_GEOMETRY:
|
||||
return make_typed_simulation_state_item<GeometrySet>(params, index);
|
||||
case SOCK_COLLECTION:
|
||||
return make_typed_simulation_state_item<Collection *>(params, index);
|
||||
case SOCK_TEXTURE:
|
||||
return make_typed_simulation_state_item<Tex *>(params, index);
|
||||
case SOCK_IMAGE:
|
||||
return make_typed_simulation_state_item<Image *>(params, index);
|
||||
case SOCK_MATERIAL:
|
||||
return make_typed_simulation_state_item<Material *>(params, index);
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return make_typed_simulation_state_item<GeometrySet>(params, index);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void copy_typed_simulation_state_output(lf::Params ¶ms, int index, const bke::sim::SimulationStateItem &state_item)
|
||||
{
|
||||
using bke::sim::TypedSimulationStateItem;
|
||||
|
||||
if (auto *typed_state_item = dynamic_cast<const bke::sim::TypedSimulationStateItem<T> *>(&state_item)) {
|
||||
params.set_output(index, typed_state_item->data());
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_simulation_state_output(
|
||||
lf::Params ¶ms,
|
||||
int index,
|
||||
short socket_type,
|
||||
const bke::sim::SimulationStateItem &state_item)
|
||||
{
|
||||
switch (socket_type) {
|
||||
case SOCK_FLOAT:
|
||||
copy_typed_simulation_state_output<ValueOrField<float>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_VECTOR:
|
||||
copy_typed_simulation_state_output<ValueOrField<float3>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_RGBA:
|
||||
copy_typed_simulation_state_output<ValueOrField<ColorGeometry4f>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_BOOLEAN:
|
||||
copy_typed_simulation_state_output<ValueOrField<bool>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_INT:
|
||||
copy_typed_simulation_state_output<ValueOrField<int>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_STRING:
|
||||
copy_typed_simulation_state_output<ValueOrField<std::string>>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_OBJECT:
|
||||
copy_typed_simulation_state_output<Object *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_GEOMETRY:
|
||||
copy_typed_simulation_state_output<GeometrySet>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_COLLECTION:
|
||||
copy_typed_simulation_state_output<Collection *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_TEXTURE:
|
||||
copy_typed_simulation_state_output<Tex *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_IMAGE:
|
||||
copy_typed_simulation_state_output<Image *>(params, index, state_item);
|
||||
break;
|
||||
case SOCK_MATERIAL:
|
||||
copy_typed_simulation_state_output<Material *>(params, index, state_item);
|
||||
break;
|
||||
default:
|
||||
Jacques Lucke
commented
Better don't support these ID types yet. They need special handling that we don't have to get into now. (have to avoid dangling pointers in the cache and stuff like that) Better don't support these ID types yet. They need special handling that we don't have to get into now. (have to avoid dangling pointers in the cache and stuff like that)
|
||||
BLI_assert_unreachable();
|
||||
copy_typed_simulation_state_output<GeometrySet>(params, index, state_item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
namespace blender::nodes::node_geo_simulation_output_cc {
|
||||
|
@ -224,14 +330,15 @@ 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(params, i, item.socket_type);
|
||||
}
|
||||
|
||||
if (all_available) {
|
||||
|
@ -242,17 +349,16 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction {
|
|||
void output_cached_state(lf::Params ¶ms, 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_output(params, i, item.socket_type, *state_item);
|
||||
}
|
||||
params.set_default_remaining_outputs();
|
||||
}
|
||||
|
|
I think for all the trivial data types, having this templated type can make sense (although I'm not sure if a generic version would be easier). Currently, I don't think that this should be used for
GeometrySet
. It feels a bit like a wrong abstraction there if it needs to be specialized below.