Simulation: remove particle nodes with outdated design

The design for how we approach the "Everything Nodes" project
has changed. We will focus on a different part of the project initially.

While future me will likely refer back to some of the code I remove here,
there is no point in keeping this code around in master currently.
It would just confuse other developers working on the project.

This does not remove the simulation modifier and data block. Those are
just cleaned up, so that the boilerplate code can be reused in the future.
This commit is contained in:
Jacques Lucke 2020-10-20 12:07:42 +02:00
parent 63a9f24b55
commit 6ced026ae1
Notes: blender-bot 2023-02-14 03:21:27 +01:00
Referenced by issue #82291, no node in simulation editor
51 changed files with 24 additions and 4651 deletions

View File

@ -573,63 +573,9 @@ class QuickLiquid(Operator):
return {'FINISHED'}
class QuickParticles(Operator):
"""Use active object as particle emitter"""
bl_idname = "object.quick_particles"
bl_label = "Quick Particles"
@classmethod
def poll(cls, context):
if not context.preferences.experimental.use_new_particle_system:
return False
if context.mode != 'OBJECT':
return False
if context.active_object is None:
return False
if context.active_object.type != 'MESH':
return False
return True
def execute(self, context):
pointcloud = bpy.data.pointclouds.new("Particles")
pointcloud_object = bpy.data.objects.new("Particles", pointcloud)
modifier = pointcloud_object.modifiers.new("Simulation", 'SIMULATION')
simulation = bpy.data.simulations.new("Particle Simulation")
tree = simulation.node_tree
default_name = "Particles"
particle_simulation_node = tree.nodes.new('SimulationNodeParticleSimulation')
particle_simulation_node.name = default_name
emitter_node = tree.nodes.new('SimulationNodeParticleMeshEmitter')
emitter_node.location.x -= 200
emitter_node.location.y += 50
emitter_node.inputs["Object"].default_value = context.active_object
force_node = tree.nodes.new('SimulationNodeForce')
force_node.location.x -= 200
force_node.location.y -= 100
force_node.inputs["Force"].default_value = (0, 0, -1)
tree.links.new(particle_simulation_node.inputs["Emitters"], emitter_node.outputs["Emitter"])
tree.links.new(particle_simulation_node.inputs["Forces"], force_node.outputs["Force"])
modifier.simulation = simulation
modifier.data_path = default_name
for obj in context.selected_objects:
obj.select_set(False)
context.collection.objects.link(pointcloud_object)
pointcloud_object.select_set(True)
context.view_layer.objects.active = pointcloud_object
pointcloud_object.show_bounds = True
return {'FINISHED'}
classes = (
QuickExplode,
QuickFur,
QuickSmoke,
QuickLiquid,
QuickParticles,
)

View File

@ -2691,8 +2691,6 @@ class VIEW3D_MT_object_quick_effects(Menu):
layout.operator("object.quick_explode")
layout.operator("object.quick_smoke")
layout.operator("object.quick_liquid")
if context.preferences.experimental.use_new_particle_system:
layout.operator("object.quick_particles")
class VIEW3D_MT_object_showhide(Menu):

View File

@ -485,64 +485,6 @@ def not_implemented_node(idname):
simulation_node_categories = [
# Simulation Nodes
SimulationNodeCategory("SIM_OUTPUT", "Output", items=[
NodeItem("SimulationNodeParticleSimulation"),
]),
SimulationNodeCategory("SIM_INPUTS", "Input", items=[
NodeItem("SimulationNodeTime"),
NodeItem("SimulationNodeParticleAttribute"),
NodeItem("FunctionNodeGroupInstanceID"),
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeObjectTransforms"),
]),
SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[
NodeItem("SimulationNodeParticleMeshEmitter"),
not_implemented_node("SimulationNodeEmitParticles"),
]),
SimulationNodeCategory("SIM_EVENTS", "Events", items=[
NodeItem("SimulationNodeParticleBirthEvent"),
NodeItem("SimulationNodeParticleTimeStepEvent"),
NodeItem("SimulationNodeAgeReachedEvent"),
not_implemented_node("SimulationNodeParticleMeshCollisionEvent"),
]),
SimulationNodeCategory("SIM_FORCES", "Forces", items=[
NodeItem("SimulationNodeForce"),
]),
SimulationNodeCategory("SIM_EXECUTE", "Execute", items=[
NodeItem("SimulationNodeSetParticleAttribute"),
NodeItem("SimulationNodeExecuteCondition"),
NodeItem("SimulationNodeKillParticle"),
not_implemented_node("SimulationNodeMultiExecute"),
]),
SimulationNodeCategory("SIM_NOISE", "Noise", items=[
not_implemented_node("ShaderNodeTexNoise"),
not_implemented_node("ShaderNodeTexWhiteNoise"),
]),
SimulationNodeCategory("SIM_COLOR", "Color", items=[
not_implemented_node("ShaderNodeMixRGB"),
not_implemented_node("ShaderNodeInvert"),
not_implemented_node("ShaderNodeHueSaturation"),
not_implemented_node("ShaderNodeGamma"),
not_implemented_node("ShaderNodeBrightContrast"),
]),
SimulationNodeCategory("SIM_CONVERTER", "Converter", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
not_implemented_node("ShaderNodeSeparateHSV"),
not_implemented_node("ShaderNodeCombineHSV"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
not_implemented_node("FunctionNodeSwitch"),
NodeItem("FunctionNodeCombineStrings"),
NodeItem("FunctionNodeRandomFloat"),
]),
SimulationNodeCategory("SIM_GROUP", "Group", items=node_group_items),
SimulationNodeCategory("SIM_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),

View File

@ -1323,27 +1323,6 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
struct MTex *mtex);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Simulation Nodes
* \{ */
#define SIM_NODE_PARTICLE_SIMULATION 1000
#define SIM_NODE_FORCE 1001
#define SIM_NODE_SET_PARTICLE_ATTRIBUTE 1002
#define SIM_NODE_PARTICLE_BIRTH_EVENT 1003
#define SIM_NODE_PARTICLE_TIME_STEP_EVENT 1004
#define SIM_NODE_EXECUTE_CONDITION 1005
#define SIM_NODE_MULTI_EXECUTE 1006
#define SIM_NODE_PARTICLE_MESH_EMITTER 1007
#define SIM_NODE_PARTICLE_MESH_COLLISION_EVENT 1008
#define SIM_NODE_EMIT_PARTICLES 1009
#define SIM_NODE_TIME 1010
#define SIM_NODE_PARTICLE_ATTRIBUTE 1011
#define SIM_NODE_AGE_REACHED_EVENT 1012
#define SIM_NODE_KILL_PARTICLE 1013
/** \} */
/* -------------------------------------------------------------------- */
/** \name Function Nodes
* \{ */

View File

@ -31,27 +31,7 @@ void *BKE_simulation_add(struct Main *bmain, const char *name);
void BKE_simulation_data_update(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Simulation *simulation);
void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain);
SimulationState *BKE_simulation_state_add(Simulation *simulation,
const char *type,
const char *name);
void BKE_simulation_state_remove(Simulation *simulation, SimulationState *state);
void BKE_simulation_state_remove_all(Simulation *simulation);
void BKE_simulation_state_reset(Simulation *simulation, SimulationState *state);
void BKE_simulation_state_reset_all(Simulation *simulation);
SimulationState *BKE_simulation_state_try_find_by_name(Simulation *simulation, const char *name);
SimulationState *BKE_simulation_state_try_find_by_name_and_type(Simulation *simulation,
const char *name,
const char *type);
void BKE_simulation_state_copy_data(const SimulationState *src_state, SimulationState *dst_state);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
template<typename StateType> const char *BKE_simulation_get_state_type_name();
#endif

View File

@ -281,10 +281,6 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
break;
}
}
@ -377,10 +373,6 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
BLI_assert(false);
break;
}
@ -723,10 +715,6 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
break;
}
}
@ -805,10 +793,6 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
break;
}
}
@ -1361,10 +1345,6 @@ static void socket_id_user_increment(bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
break;
}
}
@ -1391,10 +1371,6 @@ static void socket_id_user_decrement(bNodeSocket *sock)
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_EMITTERS:
case SOCK_EVENTS:
case SOCK_FORCES:
case SOCK_CONTROL_FLOW:
break;
}
}
@ -1522,14 +1498,6 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketObject";
case SOCK_IMAGE:
return "NodeSocketImage";
case SOCK_EMITTERS:
return "NodeSocketEmitters";
case SOCK_EVENTS:
return "NodeSocketEvents";
case SOCK_FORCES:
return "NodeSocketForces";
case SOCK_CONTROL_FLOW:
return "NodeSocketControlFlow";
}
return NULL;
}
@ -1595,14 +1563,6 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceObject";
case SOCK_IMAGE:
return "NodeSocketInterfaceImage";
case SOCK_EMITTERS:
return "NodeSocketInterfaceEmitters";
case SOCK_EVENTS:
return "NodeSocketInterfaceEvents";
case SOCK_FORCES:
return "NodeSocketInterfaceForces";
case SOCK_CONTROL_FLOW:
return "NodeSocketInterfaceControlFlow";
}
return NULL;
}
@ -4008,16 +3968,6 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
FOREACH_NODETREE_END;
}
static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree)
{
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) {
BKE_simulation_update_dependencies((Simulation *)owner_id, main);
}
}
FOREACH_NODETREE_END;
}
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
{
if (!ntree) {
@ -4073,11 +4023,6 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
ntree_validate_links(ntree);
}
if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation &&
(ntree->id.flag & LIB_EMBEDDED_DATA)) {
ntreeUpdateSimulationDependencies(bmain, ntree);
}
/* clear update flags */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->update = 0;
@ -4704,21 +4649,6 @@ static void registerTextureNodes(void)
static void registerSimulationNodes(void)
{
register_node_type_sim_group();
register_node_type_sim_particle_simulation();
register_node_type_sim_force();
register_node_type_sim_set_particle_attribute();
register_node_type_sim_particle_birth_event();
register_node_type_sim_particle_time_step_event();
register_node_type_sim_execute_condition();
register_node_type_sim_multi_execute();
register_node_type_sim_particle_mesh_emitter();
register_node_type_sim_particle_mesh_collision_event();
register_node_type_sim_emit_particles();
register_node_type_sim_time();
register_node_type_sim_particle_attribute();
register_node_type_sim_age_reached_event();
register_node_type_sim_kill_particle();
}
static void registerFunctionNodes(void)

View File

@ -61,26 +61,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "SIM_simulation_update.hh"
#include "BLO_read_write.h"
using StateInitFunction = void (*)(SimulationState *state);
using StateResetFunction = void (*)(SimulationState *state);
using StateRemoveFunction = void (*)(SimulationState *state);
using StateCopyFunction = void (*)(const SimulationState *src, SimulationState *dst);
struct SimulationStateType {
const char *name;
int size;
StateInitFunction init;
StateResetFunction reset;
StateRemoveFunction remove;
StateCopyFunction copy;
};
static const SimulationStateType *try_get_state_type(blender::StringRefNull type_name);
static void simulation_init_data(ID *id)
{
Simulation *simulation = (Simulation *)id;
@ -106,16 +88,6 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
(ID **)&simulation_dst->nodetree,
flag_private_id_data);
}
BLI_listbase_clear(&simulation_dst->states);
LISTBASE_FOREACH (const SimulationState *, state_src, &simulation_src->states) {
SimulationState *state_dst = BKE_simulation_state_add(
simulation_dst, state_src->type, state_src->name);
BKE_simulation_state_copy_data(state_src, state_dst);
}
BLI_listbase_clear(&simulation_dst->dependencies);
BLI_duplicatelist(&simulation_dst->dependencies, &simulation_src->dependencies);
}
static void simulation_free_data(ID *id)
@ -129,10 +101,6 @@ static void simulation_free_data(ID *id)
MEM_freeN(simulation->nodetree);
simulation->nodetree = nullptr;
}
BKE_simulation_state_remove_all(simulation);
BLI_freelistN(&simulation->dependencies);
}
static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
@ -142,9 +110,6 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
/* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */
BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree);
}
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
BKE_LIB_FOREACHID_PROCESS_ID(data, dependency->id, IDWALK_CB_USER);
}
}
static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_address)
@ -163,41 +128,6 @@ static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_a
BLO_write_struct(writer, bNodeTree, simulation->nodetree);
ntreeBlendWrite(writer, simulation->nodetree);
}
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
BLO_write_string(writer, state->name);
BLO_write_string(writer, state->type);
/* TODO: Decentralize this part. */
if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) {
ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
CustomData attributes_shallow_copy = particle_state->attributes;
CustomData_blend_write_prepare(
&attributes_shallow_copy, &players, players_buff, ARRAY_SIZE(players_buff));
BLO_write_struct(writer, ParticleSimulationState, particle_state);
CustomData_blend_write(writer,
&attributes_shallow_copy,
players,
particle_state->tot_particles,
CD_MASK_ALL,
&simulation->id);
/* Remove temporary data. */
if (players && players != players_buff) {
MEM_freeN(players);
}
}
else if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER)) {
ParticleMeshEmitterSimulationState *emitter_state = (ParticleMeshEmitterSimulationState *)
state;
BLO_write_struct(writer, ParticleMeshEmitterSimulationState, emitter_state);
}
}
BLO_write_struct_list(writer, SimulationDependency, &simulation->dependencies);
}
}
@ -206,34 +136,18 @@ static void simulation_blend_read_data(BlendDataReader *reader, ID *id)
Simulation *simulation = (Simulation *)id;
BLO_read_data_address(reader, &simulation->adt);
BKE_animdata_blend_read_data(reader, simulation->adt);
BLO_read_list(reader, &simulation->states);
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
BLO_read_data_address(reader, &state->name);
BLO_read_data_address(reader, &state->type);
if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) {
ParticleSimulationState *particle_state = (ParticleSimulationState *)state;
CustomData_blend_read(reader, &particle_state->attributes, particle_state->tot_particles);
}
}
BLO_read_list(reader, &simulation->dependencies);
}
static void simulation_blend_read_lib(BlendLibReader *reader, ID *id)
{
Simulation *simulation = (Simulation *)id;
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
BLO_read_id_address(reader, simulation->id.lib, &dependency->id);
}
UNUSED_VARS(simulation, reader);
}
static void simulation_blend_read_expand(BlendExpander *expander, ID *id)
{
Simulation *simulation = (Simulation *)id;
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
BLO_expand(expander, dependency->id);
}
UNUSED_VARS(simulation, expander);
}
IDTypeInfo IDType_ID_SIM = {
@ -262,204 +176,11 @@ IDTypeInfo IDType_ID_SIM = {
void *BKE_simulation_add(Main *bmain, const char *name)
{
Simulation *simulation = (Simulation *)BKE_id_new(bmain, ID_SIM, name);
return simulation;
}
SimulationState *BKE_simulation_state_add(Simulation *simulation,
const char *type,
const char *name)
void BKE_simulation_data_update(Depsgraph *UNUSED(depsgraph),
Scene *UNUSED(scene),
Simulation *UNUSED(simulation))
{
BLI_assert(simulation != nullptr);
BLI_assert(name != nullptr);
const SimulationStateType *state_type = try_get_state_type(type);
BLI_assert(state_type != nullptr);
SimulationState *state = (SimulationState *)MEM_callocN(state_type->size, AT);
state->type = BLI_strdup(type);
state->name = BLI_strdup(name);
state_type->init(state);
BLI_addtail(&simulation->states, state);
return state;
}
void BKE_simulation_state_remove(Simulation *simulation, SimulationState *state)
{
BLI_assert(simulation != nullptr);
BLI_assert(state != nullptr);
BLI_assert(BLI_findindex(&simulation->states, state) >= 0);
BLI_remlink(&simulation->states, state);
const SimulationStateType *state_type = try_get_state_type(state->type);
BLI_assert(state_type != nullptr);
state_type->remove(state);
MEM_freeN(state->name);
MEM_freeN(state->type);
MEM_freeN(state);
}
void BKE_simulation_state_remove_all(Simulation *simulation)
{
BLI_assert(simulation != nullptr);
while (!BLI_listbase_is_empty(&simulation->states)) {
BKE_simulation_state_remove(simulation, (SimulationState *)simulation->states.first);
}
}
void BKE_simulation_state_reset(Simulation *UNUSED(simulation), SimulationState *state)
{
BLI_assert(state != nullptr);
const SimulationStateType *state_type = try_get_state_type(state->type);
BLI_assert(state_type != nullptr);
state_type->reset(state);
}
void BKE_simulation_state_reset_all(Simulation *simulation)
{
BLI_assert(simulation != nullptr);
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
BKE_simulation_state_reset(simulation, state);
}
}
void BKE_simulation_state_copy_data(const SimulationState *src_state, SimulationState *dst_state)
{
BLI_assert(src_state != nullptr);
BLI_assert(dst_state != nullptr);
BLI_assert(STREQ(src_state->type, dst_state->type));
const SimulationStateType *state_type = try_get_state_type(src_state->type);
BLI_assert(state_type != nullptr);
state_type->copy(src_state, dst_state);
}
SimulationState *BKE_simulation_state_try_find_by_name(Simulation *simulation, const char *name)
{
if (simulation == nullptr) {
return nullptr;
}
if (name == nullptr) {
return nullptr;
}
LISTBASE_FOREACH (SimulationState *, state, &simulation->states) {
if (STREQ(state->name, name)) {
return state;
}
}
return nullptr;
}
SimulationState *BKE_simulation_state_try_find_by_name_and_type(Simulation *simulation,
const char *name,
const char *type)
{
if (type == nullptr) {
return nullptr;
}
SimulationState *state = BKE_simulation_state_try_find_by_name(simulation, name);
if (state == nullptr) {
return nullptr;
}
if (STREQ(state->type, type)) {
return state;
}
return nullptr;
}
void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation)
{
blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation);
}
void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain)
{
bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation);
if (dependencies_changed) {
DEG_relations_tag_update(bmain);
}
}
using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>;
template<typename T>
static void add_state_type(StateTypeMap &map,
void (*init)(T *state),
void (*reset)(T *state),
void (*remove)(T *state),
void (*copy)(const T *src, T *dst))
{
SimulationStateType state_type{
BKE_simulation_get_state_type_name<T>(),
static_cast<int>(sizeof(T)),
(StateInitFunction)init,
(StateResetFunction)reset,
(StateRemoveFunction)remove,
(StateCopyFunction)copy,
};
map.add_new(state_type.name, std::make_unique<SimulationStateType>(state_type));
}
static StateTypeMap init_state_types()
{
StateTypeMap map;
add_state_type<ParticleSimulationState>(
map,
[](ParticleSimulationState *state) { CustomData_reset(&state->attributes); },
[](ParticleSimulationState *state) {
CustomData_free(&state->attributes, state->tot_particles);
state->tot_particles = 0;
state->next_particle_id = 0;
},
[](ParticleSimulationState *state) {
CustomData_free(&state->attributes, state->tot_particles);
},
[](const ParticleSimulationState *src, ParticleSimulationState *dst) {
CustomData_free(&dst->attributes, dst->tot_particles);
dst->tot_particles = src->tot_particles;
dst->next_particle_id = src->next_particle_id;
CustomData_copy(
&src->attributes, &dst->attributes, CD_MASK_ALL, CD_DUPLICATE, src->tot_particles);
});
add_state_type<ParticleMeshEmitterSimulationState>(
map,
[](ParticleMeshEmitterSimulationState *UNUSED(state)) {},
[](ParticleMeshEmitterSimulationState *state) { state->last_birth_time = 0.0f; },
[](ParticleMeshEmitterSimulationState *UNUSED(state)) {},
[](const ParticleMeshEmitterSimulationState *src, ParticleMeshEmitterSimulationState *dst) {
dst->last_birth_time = src->last_birth_time;
});
return map;
}
static StateTypeMap &get_state_types()
{
static StateTypeMap state_type_map = init_state_types();
return state_type_map;
}
static const SimulationStateType *try_get_state_type(blender::StringRefNull type_name)
{
std::unique_ptr<SimulationStateType> *type = get_state_types().lookup_ptr_as(type_name);
if (type == nullptr) {
return nullptr;
}
return type->get();
}
template<> const char *BKE_simulation_get_state_type_name<ParticleSimulationState>()
{
return SIM_TYPE_NAME_PARTICLE_SIMULATION;
}
template<> const char *BKE_simulation_get_state_type_name<ParticleMeshEmitterSimulationState>()
{
return SIM_TYPE_NAME_PARTICLE_MESH_EMITTER;
}

View File

@ -2660,24 +2660,6 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation)
OperationKey nodetree_key(
&simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0);
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) {
if (dependency->id == nullptr) {
continue;
}
build_id(dependency->id);
if (GS(dependency->id->name) == ID_OB) {
Object *object = (Object *)dependency->id;
if (dependency->flag & SIM_DEPENDS_ON_TRANSFORM) {
ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM);
add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation");
}
if (dependency->flag & SIM_DEPENDS_ON_GEOMETRY) {
ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY);
add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation");
}
}
}
}
void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)

View File

@ -3140,58 +3140,8 @@ static void node_texture_set_butfunc(bNodeType *ntype)
/* ****************** BUTTON CALLBACKS FOR SIMULATION NODES ***************** */
static void node_simulation_buts_particle_simulation(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
static void node_simulation_set_butfunc(bNodeType *UNUSED(ntype))
{
uiItemR(layout, ptr, "name", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_simulation_buts_particle_time_step_event(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_simulation_buts_particle_attribute(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_simulation_buts_set_particle_attribute(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_simulation_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE);
}
static void node_simulation_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
case SIM_NODE_PARTICLE_SIMULATION:
ntype->draw_buttons = node_simulation_buts_particle_simulation;
break;
case SIM_NODE_PARTICLE_TIME_STEP_EVENT:
ntype->draw_buttons = node_simulation_buts_particle_time_step_event;
break;
case SIM_NODE_PARTICLE_ATTRIBUTE:
ntype->draw_buttons = node_simulation_buts_particle_attribute;
break;
case SIM_NODE_SET_PARTICLE_ATTRIBUTE:
ntype->draw_buttons = node_simulation_buts_set_particle_attribute;
break;
case SIM_NODE_TIME:
ntype->draw_buttons = node_simulation_buts_time;
break;
}
}
/* ****************** BUTTON CALLBACKS FOR FUNCTION NODES ***************** */
@ -3379,10 +3329,6 @@ static const float std_node_socket_colors[][4] = {
{0.39, 0.39, 0.39, 1.0}, /* SOCK_STRING */
{0.40, 0.10, 0.10, 1.0}, /* SOCK_OBJECT */
{0.10, 0.40, 0.10, 1.0}, /* SOCK_IMAGE */
{0.80, 0.80, 0.20, 1.0}, /* SOCK_EMITTERS */
{0.80, 0.20, 0.80, 1.0}, /* SOCK_EVENTS */
{0.20, 0.80, 0.80, 1.0}, /* SOCK_FORCES */
{0.30, 0.30, 0.30, 1.0}, /* SOCK_CONTROL_FLOW */
};
/* common color callbacks for standard types */

View File

@ -446,7 +446,7 @@ typedef enum ID_Type {
ID_HA = MAKE_ID2('H', 'A'), /* Hair */
ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
ID_VO = MAKE_ID2('V', 'O'), /* Volume */
ID_SIM = MAKE_ID2('S', 'I'), /* Simulation */
ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (currently unused) */
} ID_Type;
/* Only used as 'placeholder' in .blend files for directly linked data-blocks. */

View File

@ -576,8 +576,6 @@
#define _DNA_DEFAULT_SimulationModifierData \
{ \
.simulation = NULL, \
.data_path = NULL, \
}
#define _DNA_DEFAULT_SkinModifierData \

View File

@ -2219,9 +2219,6 @@ enum {
typedef struct SimulationModifierData {
ModifierData modifier;
struct Simulation *simulation;
char *data_path;
} SimulationModifierData;
typedef struct MeshToVolumeModifierData {

View File

@ -155,10 +155,6 @@ typedef enum eNodeSocketDatatype {
SOCK_STRING = 7,
SOCK_OBJECT = 8,
SOCK_IMAGE = 9,
SOCK_EMITTERS = 10,
SOCK_EVENTS = 11,
SOCK_FORCES = 12,
SOCK_CONTROL_FLOW = 13,
} eNodeSocketDatatype;
/* socket shape */

View File

@ -27,69 +27,14 @@ typedef struct Simulation {
ID id;
struct AnimData *adt; /* animation data (must be immediately after id) */
/* This nodetree is embedded into the data block. */
struct bNodeTree *nodetree;
uint32_t flag;
/** This is the frame in scene time, that the states correspond to. */
float current_frame;
/** Time since the start of the simulation in simulation time (which might differ from scene
* time). */
float current_simulation_time;
char _pad[4];
/** List containing SimulationState objects. */
struct ListBase states;
/** List containing SimulationDependency objects. */
struct ListBase dependencies;
} Simulation;
typedef struct SimulationState {
struct SimulationState *next;
struct SimulationState *prev;
char *type;
char *name;
} SimulationState;
typedef struct ParticleSimulationState {
SimulationState head;
/** Contains the state of the particles at time Simulation->current_frame. */
int32_t tot_particles;
int32_t next_particle_id;
struct CustomData attributes;
} ParticleSimulationState;
typedef struct ParticleMeshEmitterSimulationState {
SimulationState head;
float last_birth_time;
char _pad[4];
} ParticleMeshEmitterSimulationState;
/** Stores a reference to data that the simulation depends on. This is partially derived from the
* simulation node tree. */
typedef struct SimulationDependency {
struct SimulationDependency *next;
struct SimulationDependency *prev;
struct ID *id;
int32_t handle;
uint32_t flag;
} SimulationDependency;
/* Simulation.flag */
enum {
SIM_DS_EXPAND = (1 << 0),
};
/* SimulationDependency.flag */
enum {
SIM_DEPENDS_ON_TRANSFORM = (1 << 0),
SIM_DEPENDS_ON_GEOMETRY = (1 << 1),
};
#define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation"
#define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter"

View File

@ -1531,51 +1531,6 @@ static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr,
CLAMP_MIN(psmd->psys, 1);
}
# ifdef WITH_PARTICLE_NODES
static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
SimulationModifierData *smd = ptr->data;
if (smd->simulation != NULL) {
DEG_id_tag_update(&smd->simulation->id, ID_RECALC_ALL);
}
rna_Modifier_dependency_update(bmain, scene, ptr);
}
static void rna_SimulationModifier_data_path_get(PointerRNA *ptr, char *value)
{
SimulationModifierData *smd = ptr->data;
if (smd->data_path) {
strcpy(value, smd->data_path);
}
else {
value[0] = '\0';
}
}
static int rna_SimulationModifier_data_path_length(PointerRNA *ptr)
{
SimulationModifierData *smd = ptr->data;
return smd->data_path ? strlen(smd->data_path) : 0;
}
static void rna_SimulationModifier_data_path_set(PointerRNA *ptr, const char *value)
{
SimulationModifierData *smd = ptr->data;
if (smd->data_path) {
MEM_freeN(smd->data_path);
}
if (value[0]) {
smd->data_path = BLI_strdup(value);
}
else {
smd->data_path = NULL;
}
}
# endif
/**
* Special set callback that just changes the first bit of the expansion flag.
* This way the expansion state of all the sub-panels is not changed by RNA.
@ -6968,7 +6923,6 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
static void rna_def_modifier_simulation(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "SimulationModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Simulation Modifier", "");
@ -6977,20 +6931,6 @@ static void rna_def_modifier_simulation(BlenderRNA *brna)
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "simulation", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Simulation", "Simulation to access");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, 0, "rna_SimulationModifier_simulation_update");
prop = RNA_def_property(srna, "data_path", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
"rna_SimulationModifier_data_path_get",
"rna_SimulationModifier_data_path_length",
"rna_SimulationModifier_data_path_set");
RNA_def_property_ui_text(
prop, "Data Path", "Identifier of the simulation component that should be accessed");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
# endif

View File

@ -84,21 +84,6 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_SHADER, "SHADER", 0, "Shader", ""},
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_EMITTERS, "EMITTERS", 0, "Emitters", ""},
{SOCK_EVENTS, "EVENTS", 0, "Events", ""},
{SOCK_FORCES, "FORCES", 0, "Forces", ""},
{SOCK_CONTROL_FLOW, "CONTROL_FLOW", 0, "Control Flow", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem particle_attribute_socket_type_items[] = {
{SOCK_FLOAT, "FLOAT", 0, "Float", ""},
{SOCK_INT, "INT", 0, "Int", ""},
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_RGBA, "RGBA", 0, "Color", ""},
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{0, NULL, 0, NULL, NULL},
};
@ -3758,15 +3743,6 @@ static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA
rna_Node_update(bmain, scene, ptr);
}
static void rna_SimulationNode_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
nodeUpdate(ntree, node);
rna_Node_update(bmain, scene, ptr);
}
static PointerRNA rna_ShaderNodePointDensity_psys_get(PointerRNA *ptr)
{
bNode *node = ptr->data;
@ -8164,82 +8140,6 @@ static void def_tex_bricks(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
/* -- Simulation Nodes --------------------------------------------------------- */
static void def_sim_particle_time_step_event(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
{NODE_PARTICLE_TIME_STEP_EVENT_BEGIN,
"BEGIN",
0,
"Begin",
"Execute for every particle at the beginning of each time step"},
{NODE_PARTICLE_TIME_STEP_EVENT_END,
"END",
0,
"End",
"Execute for every particle at the end of each time step"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "When in each time step is the event triggered");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_sim_particle_attribute(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, particle_attribute_socket_type_items);
RNA_def_property_ui_text(
prop,
"Data Type",
"Expected type of the attribute. A default value is returned if the type is not correct");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationNode_socket_update");
}
static void def_sim_set_particle_attribute(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, particle_attribute_socket_type_items);
RNA_def_property_ui_text(
prop,
"Data Type",
"Expected type of the attribute. Nothing is done if the type is not correct");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationNode_socket_update");
}
static void def_sim_time(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
{NODE_SIM_INPUT_SIMULATION_TIME,
"SIMULATION_TIME",
0,
"Simulation Time",
"Time since start of simulation"},
{NODE_SIM_INPUT_SCENE_TIME, "SCENE_TIME", 0, "Scene Time", "Time shown in the timeline"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "The time to output");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationNode_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@ -74,18 +74,19 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimulationModifierData), modifier);
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *UNUSED(ctx))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
if (smd->simulation) {
DEG_add_simulation_relation(ctx->node, smd->simulation, "Accessed Simulation");
}
UNUSED_VARS(smd);
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
static void foreachIDLink(ModifierData *md,
Object *UNUSED(ob),
IDWalkFunc UNUSED(walk),
void *UNUSED(userData))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
walk(userData, ob, (ID **)&smd->simulation, IDWALK_CB_USER);
UNUSED_VARS(smd);
}
static bool isDisabled(const struct Scene *UNUSED(scene),
@ -93,45 +94,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
bool UNUSED(useRenderParams))
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
return smd->simulation == nullptr;
}
static const ParticleSimulationState *find_particle_state(SimulationModifierData *smd)
{
return reinterpret_cast<const ParticleSimulationState *>(
BKE_simulation_state_try_find_by_name_and_type(
smd->simulation, smd->data_path, SIM_TYPE_NAME_PARTICLE_SIMULATION));
UNUSED_VARS(smd);
return false;
}
static PointCloud *modifyPointCloud(ModifierData *md,
const ModifierEvalContext *UNUSED(ctx),
PointCloud *input_pointcloud)
PointCloud *pointcloud)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
const ParticleSimulationState *state = find_particle_state(smd);
if (state == nullptr) {
return input_pointcloud;
}
PointCloud *pointcloud = BKE_pointcloud_new_for_eval(input_pointcloud, state->tot_particles);
if (state->tot_particles == 0) {
return pointcloud;
}
const float3 *positions = static_cast<const float3 *>(
CustomData_get_layer_named(&state->attributes, CD_PROP_FLOAT3, "Position"));
const float *radii = static_cast<const float *>(
CustomData_get_layer_named(&state->attributes, CD_PROP_FLOAT, "Radius"));
memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles);
CustomData_add_layer_named(
&pointcloud->pdata, CD_PROP_FLOAT, CD_CALLOC, NULL, state->tot_particles, "Radius");
BKE_pointcloud_update_customdata_pointers(pointcloud);
for (int i = 0; i < state->tot_particles; i++) {
pointcloud->radius[i] = radii[i];
}
UNUSED_VARS(smd);
return pointcloud;
}
@ -145,8 +117,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "simulation", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "data_path", 0, NULL, ICON_NONE);
uiItemL(layout, "This modifier does nothing currently", ICON_INFO);
modifier_panel_end(layout, ptr);
}
@ -159,32 +130,28 @@ static void panelRegister(ARegionType *region_type)
static void blendWrite(BlendWriter *writer, const ModifierData *md)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
BLO_write_string(writer, smd->data_path);
UNUSED_VARS(smd, writer);
}
static void blendRead(BlendDataReader *reader, ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
BLO_read_data_address(reader, &smd->data_path);
UNUSED_VARS(smd, reader);
}
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
SimulationModifierData *tsmd = reinterpret_cast<SimulationModifierData *>(target);
UNUSED_VARS(smd, tsmd);
BKE_modifier_copydata_generic(md, target, flag);
if (smd->data_path != nullptr) {
tsmd->data_path = BLI_strdup(smd->data_path);
}
}
static void freeData(ModifierData *md)
{
SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
if (smd->data_path) {
MEM_freeN(smd->data_path);
}
UNUSED_VARS(smd);
}
ModifierTypeInfo modifierType_Simulation = {

View File

@ -230,21 +230,7 @@ set(SRC
shader/node_shader_tree.c
shader/node_shader_util.c
simulation/nodes/node_sim_age_reached_event.cc
simulation/nodes/node_sim_common.cc
simulation/nodes/node_sim_emit_particles.cc
simulation/nodes/node_sim_execute_condition.cc
simulation/nodes/node_sim_force.cc
simulation/nodes/node_sim_kill_particle.cc
simulation/nodes/node_sim_multi_execute.cc
simulation/nodes/node_sim_particle_attribute.cc
simulation/nodes/node_sim_particle_birth_event.cc
simulation/nodes/node_sim_particle_mesh_collision_event.cc
simulation/nodes/node_sim_particle_mesh_emitter.cc
simulation/nodes/node_sim_particle_simulation.cc
simulation/nodes/node_sim_particle_time_step_event.cc
simulation/nodes/node_sim_set_particle_attribute.cc
simulation/nodes/node_sim_simulation_time.cc
simulation/node_simulation_tree.cc
simulation/node_simulation_util.cc

View File

@ -26,21 +26,6 @@ void register_node_tree_type_sim(void);
void register_node_type_sim_group(void);
void register_node_type_sim_particle_simulation(void);
void register_node_type_sim_force(void);
void register_node_type_sim_set_particle_attribute(void);
void register_node_type_sim_particle_birth_event(void);
void register_node_type_sim_particle_time_step_event(void);
void register_node_type_sim_execute_condition(void);
void register_node_type_sim_multi_execute(void);
void register_node_type_sim_particle_mesh_emitter(void);
void register_node_type_sim_particle_mesh_collision_event(void);
void register_node_type_sim_emit_particles(void);
void register_node_type_sim_time(void);
void register_node_type_sim_particle_attribute(void);
void register_node_type_sim_age_reached_event(void);
void register_node_type_sim_kill_particle(void);
#ifdef __cplusplus
}
#endif

View File

@ -258,21 +258,6 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
DefNode(SimulationNode, SIM_NODE_PARTICLE_SIMULATION, 0, "PARTICLE_SIMULATION", ParticleSimulation, "Particle Simulation", "")
DefNode(SimulationNode, SIM_NODE_FORCE, 0, "FORCE", Force, "Force", "")
DefNode(SimulationNode, SIM_NODE_SET_PARTICLE_ATTRIBUTE, def_sim_set_particle_attribute, "SET_PARTICLE_ATTRIBUTE", SetParticleAttribute, "Set Particle Attribute", "")
DefNode(SimulationNode, SIM_NODE_PARTICLE_BIRTH_EVENT, 0, "PARTICLE_BIRTH_EVENT", ParticleBirthEvent, "Particle Birth Event", "")
DefNode(SimulationNode, SIM_NODE_PARTICLE_TIME_STEP_EVENT, def_sim_particle_time_step_event, "PARTICLE_TIME_STEP_EVENT", ParticleTimeStepEvent, "Particle Time Step Event", "")
DefNode(SimulationNode, SIM_NODE_EXECUTE_CONDITION, 0, "EXECUTE_CONDITION", ExecuteCondition, "Execute Condition", "")
DefNode(SimulationNode, SIM_NODE_MULTI_EXECUTE, 0, "MULTI_EXECUTE", MultiExecute, "Multi Execute", "")
DefNode(SimulationNode, SIM_NODE_PARTICLE_MESH_EMITTER, 0, "PARTICLE_MESH_EMITTER", ParticleMeshEmitter, "Particle Mesh Emitter", "")
DefNode(SimulationNode, SIM_NODE_PARTICLE_MESH_COLLISION_EVENT, 0, "PARTICLE_MESH_COLLISION_EVENT", ParticleMeshCollisionEvent, "Particle Mesh Collision Event", "")
DefNode(SimulationNode, SIM_NODE_EMIT_PARTICLES, 0, "EMIT_PARTICLES", EmitParticles, "Emit Particles", "")
DefNode(SimulationNode, SIM_NODE_TIME, def_sim_time, "TIME", Time, "Time", "")
DefNode(SimulationNode, SIM_NODE_PARTICLE_ATTRIBUTE, def_sim_particle_attribute, "PARTICLE_ATTRIBUTE", ParticleAttribute, "Particle Attribute", "")
DefNode(SimulationNode, SIM_NODE_AGE_REACHED_EVENT, 0, "AGE_REACHED_EVENT", AgeReachedEvent, "Age Reached Event", "")
DefNode(SimulationNode, SIM_NODE_KILL_PARTICLE, 0, "KILL_PARTICLE", KillParticle, "Kill Particle", "")
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", Switch, "Switch", "")

View File

@ -549,19 +549,6 @@ static bNodeSocketType *make_socket_type_virtual(void)
return stype;
}
static bNodeSocketType *make_socket_type_effector(int type)
{
bNodeSocketType *stype = make_standard_socket_type(type, PROP_NONE);
stype->input_link_limit = 0xFFF;
return stype;
}
static bNodeSocketType *make_socket_type_control_flow(int type)
{
bNodeSocketType *stype = make_standard_socket_type(type, PROP_NONE);
return stype;
}
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
@ -721,11 +708,5 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE));
nodeRegisterSocketType(make_socket_type_effector(SOCK_EMITTERS));
nodeRegisterSocketType(make_socket_type_effector(SOCK_EVENTS));
nodeRegisterSocketType(make_socket_type_effector(SOCK_FORCES));
nodeRegisterSocketType(make_socket_type_control_flow(SOCK_CONTROL_FLOW));
nodeRegisterSocketType(make_socket_type_virtual());
}

View File

@ -1,38 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_age_reached_event_in[] = {
{SOCK_FLOAT, N_("Age"), 3, 0, 0, 0, 0, 10000000},
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_age_reached_event_out[] = {
{SOCK_EVENTS, N_("Event")},
{-1, ""},
};
void register_node_type_sim_age_reached_event()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_AGE_REACHED_EVENT, "Age Reached Event", 0, 0);
node_type_socket_templates(
&ntype, sim_node_age_reached_event_in, sim_node_age_reached_event_out);
nodeRegisterType(&ntype);
}

View File

@ -1,38 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_emit_particle_in[] = {
{SOCK_INT, N_("Amount"), 10, 0, 0, 0, 0, 10000000},
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_emit_particle_out[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{SOCK_EMITTERS, N_("Emitter")},
{-1, ""},
};
void register_node_type_sim_emit_particles()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_EMIT_PARTICLES, "Emit Particles", 0, 0);
node_type_socket_templates(&ntype, sim_node_emit_particle_in, sim_node_emit_particle_out);
nodeRegisterType(&ntype);
}

View File

@ -1,39 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_execute_condition_in[] = {
{SOCK_BOOLEAN, N_("Condition")},
{SOCK_CONTROL_FLOW, N_("If True")},
{SOCK_CONTROL_FLOW, N_("If False")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_execute_condition_out[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
void register_node_type_sim_execute_condition()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_EXECUTE_CONDITION, "Execute Condition", 0, 0);
node_type_socket_templates(
&ntype, sim_node_execute_condition_in, sim_node_execute_condition_out);
nodeRegisterType(&ntype);
}

View File

@ -1,36 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_force_in[] = {
{SOCK_VECTOR, N_("Force"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{-1, ""},
};
static bNodeSocketTemplate sim_node_force_out[] = {
{SOCK_FORCES, N_("Force")},
{-1, ""},
};
void register_node_type_sim_force()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_FORCE, "Force", 0, 0);
node_type_socket_templates(&ntype, sim_node_force_in, sim_node_force_out);
nodeRegisterType(&ntype);
}

View File

@ -1,36 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_kill_particle_in[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_kill_particle_out[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
void register_node_type_sim_kill_particle()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_KILL_PARTICLE, "Kill Particle", 0, 0);
node_type_socket_templates(&ntype, sim_node_kill_particle_in, sim_node_kill_particle_out);
nodeRegisterType(&ntype);
}

View File

@ -1,38 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_multi_execute_in[] = {
{SOCK_CONTROL_FLOW, "1"},
{SOCK_CONTROL_FLOW, "2"},
{SOCK_CONTROL_FLOW, "3"},
{-1, ""},
};
static bNodeSocketTemplate sim_node_multi_execute_out[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
void register_node_type_sim_multi_execute()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_MULTI_EXECUTE, "Multi Execute", 0, 0);
node_type_socket_templates(&ntype, sim_node_multi_execute_in, sim_node_multi_execute_out);
nodeRegisterType(&ntype);
}

View File

@ -1,52 +0,0 @@
/*
* 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 "BLI_listbase.h"
#include "node_simulation_util.h"
static bNodeSocketTemplate sim_node_particle_attribute_in[] = {
{SOCK_STRING, N_("Name")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_attribute_out[] = {
{SOCK_FLOAT, N_("Float")},
{SOCK_INT, N_("Int")},
{SOCK_BOOLEAN, N_("Boolean")},
{SOCK_VECTOR, N_("Vector")},
{SOCK_RGBA, N_("Color")},
{SOCK_OBJECT, N_("Object")},
{SOCK_IMAGE, N_("Image")},
{-1, ""},
};
static void sim_node_particle_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
nodeSetSocketAvailability(sock, sock->type == node->custom1);
}
}
void register_node_type_sim_particle_attribute()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_PARTICLE_ATTRIBUTE, "Particle Attribute", 0, 0);
node_type_socket_templates(
&ntype, sim_node_particle_attribute_in, sim_node_particle_attribute_out);
node_type_update(&ntype, sim_node_particle_attribute_update);
nodeRegisterType(&ntype);
}

View File

@ -1,37 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_particle_birth_event_in[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_birth_event_out[] = {
{SOCK_EVENTS, N_("Event")},
{-1, ""},
};
void register_node_type_sim_particle_birth_event()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_PARTICLE_BIRTH_EVENT, "Particle Birth Event", 0, 0);
node_type_socket_templates(
&ntype, sim_node_particle_birth_event_in, sim_node_particle_birth_event_out);
nodeRegisterType(&ntype);
}

View File

@ -1,40 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_particle_mesh_collision_event_in[] = {
{SOCK_OBJECT, N_("Object")},
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_mesh_collision_event_out[] = {
{SOCK_EVENTS, N_("Event")},
{-1, ""},
};
void register_node_type_sim_particle_mesh_collision_event()
{
static bNodeType ntype;
sim_node_type_base(
&ntype, SIM_NODE_PARTICLE_MESH_COLLISION_EVENT, "Particle Mesh Collision Event", 0, 0);
node_type_socket_templates(&ntype,
sim_node_particle_mesh_collision_event_in,
sim_node_particle_mesh_collision_event_out);
nodeRegisterType(&ntype);
}

View File

@ -1,41 +0,0 @@
/*
* 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 "node_simulation_util.h"
#include "float.h"
static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = {
{SOCK_OBJECT, N_("Object")},
{SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_mesh_emitter_out[] = {
{SOCK_EMITTERS, N_("Emitter")},
{-1, ""},
};
void register_node_type_sim_particle_mesh_emitter()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_PARTICLE_MESH_EMITTER, "Mesh Emitter", 0, 0);
node_type_socket_templates(
&ntype, sim_node_particle_mesh_emitter_in, sim_node_particle_mesh_emitter_out);
nodeRegisterType(&ntype);
}

View File

@ -1,39 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_particle_simulation_in[] = {
{SOCK_EMITTERS, N_("Emitters")},
{SOCK_EVENTS, N_("Events")},
{SOCK_FORCES, N_("Forces")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_simulation_out[] = {
{-1, ""},
};
void register_node_type_sim_particle_simulation()
{
static bNodeType ntype;
sim_node_type_base(
&ntype, SIM_NODE_PARTICLE_SIMULATION, "Particle Simulation", NODE_CLASS_OUTPUT, 0);
node_type_socket_templates(
&ntype, sim_node_particle_simulation_in, sim_node_particle_simulation_out);
nodeRegisterType(&ntype);
}

View File

@ -1,37 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_particle_time_step_event_in[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_particle_time_step_event_out[] = {
{SOCK_EVENTS, N_("Event")},
{-1, ""},
};
void register_node_type_sim_particle_time_step_event()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_PARTICLE_TIME_STEP_EVENT, "Particle Time Step Event", 0, 0);
node_type_socket_templates(
&ntype, sim_node_particle_time_step_event_in, sim_node_particle_time_step_event_out);
nodeRegisterType(&ntype);
}

View File

@ -1,58 +0,0 @@
/*
* 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 "BLI_listbase.h"
#include "node_simulation_util.h"
static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{SOCK_STRING, N_("Name")},
{SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000},
{SOCK_BOOLEAN, N_("Boolean")},
{SOCK_VECTOR, N_("Vector")},
{SOCK_RGBA, N_("Color")},
{SOCK_OBJECT, N_("Object")},
{SOCK_IMAGE, N_("Image")},
{-1, ""},
};
static bNodeSocketTemplate sim_node_set_particle_attribute_out[] = {
{SOCK_CONTROL_FLOW, N_("Execute")},
{-1, ""},
};
static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
{
int index = 0;
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (index >= 2) {
nodeSetSocketAvailability(sock, sock->type == node->custom1);
}
index++;
}
}
void register_node_type_sim_set_particle_attribute()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_SET_PARTICLE_ATTRIBUTE, "Set Particle Attribute", 0, 0);
node_type_socket_templates(
&ntype, sim_node_set_particle_attribute_in, sim_node_set_particle_attribute_out);
node_type_update(&ntype, sim_node_set_particle_attribute_update);
nodeRegisterType(&ntype);
}

View File

@ -1,31 +0,0 @@
/*
* 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 "node_simulation_util.h"
static bNodeSocketTemplate sim_node_time_out[] = {
{SOCK_FLOAT, N_("Time")},
{-1, ""},
};
void register_node_type_sim_time()
{
static bNodeType ntype;
sim_node_type_base(&ntype, SIM_NODE_TIME, "Time", 0, 0);
node_type_socket_templates(&ntype, nullptr, sim_node_time_out);
nodeRegisterType(&ntype);
}

View File

@ -41,32 +41,16 @@ set(SRC
intern/hair_volume.cpp
intern/implicit_blender.c
intern/implicit_eigen.cpp
intern/particle_allocator.cc
intern/particle_function.cc
intern/particle_mesh_emitter.cc
intern/simulation_collect_influences.cc
intern/simulation_solver.cc
intern/simulation_solver_influences.cc
intern/simulation_update.cc
intern/ConstrainedConjugateGradient.h
intern/eigen_utils.h
intern/implicit.h
intern/particle_allocator.hh
intern/particle_function.hh
intern/particle_mesh_emitter.hh
intern/simulation_collect_influences.hh
intern/simulation_solver.hh
intern/simulation_solver_influences.hh
intern/time_interval.hh
SIM_mass_spring.h
SIM_simulation_update.hh
)
set(LIB
bf_blenkernel
bf_nodes
)
if(WITH_OPENMP_STATIC)

View File

@ -1,31 +0,0 @@
/*
* 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.
*/
#pragma once
struct Depsgraph;
struct Scene;
struct Simulation;
namespace blender::sim {
void update_simulation_in_depsgraph(Depsgraph *depsgraph,
Scene *scene_cow,
Simulation *simulation_cow);
bool update_simulation_dependencies(Simulation *simulation);
} // namespace blender::sim

View File

@ -1,86 +0,0 @@
/*
* 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 "particle_allocator.hh"
#include "BLI_rand.hh"
namespace blender::sim {
AttributesAllocator::~AttributesAllocator()
{
for (std::unique_ptr<AttributesBlock> &block : allocated_blocks_) {
for (int i : attributes_info_.index_range()) {
const fn::CPPType &type = attributes_info_.type_of(i);
type.destruct_n(block->buffers[i], block->size);
MEM_freeN(block->buffers[i]);
}
}
}
fn::MutableAttributesRef AttributesAllocator::allocate_uninitialized(int size)
{
std::unique_ptr<AttributesBlock> block = std::make_unique<AttributesBlock>();
block->buffers = Array<void *>(attributes_info_.size(), nullptr);
block->size = size;
for (int i : attributes_info_.index_range()) {
const fn::CPPType &type = attributes_info_.type_of(i);
void *buffer = MEM_mallocN_aligned(size * type.size(), type.alignment(), AT);
block->buffers[i] = buffer;
}
fn::MutableAttributesRef attributes{attributes_info_, block->buffers, size};
{
std::lock_guard lock{mutex_};
allocated_blocks_.append(std::move(block));
allocated_attributes_.append(attributes);
total_allocated_ += size;
}
return attributes;
}
fn::MutableAttributesRef ParticleAllocator::allocate(int size)
{
const fn::AttributesInfo &info = attributes_allocator_.attributes_info();
fn::MutableAttributesRef attributes = attributes_allocator_.allocate_uninitialized(size);
for (int i : info.index_range()) {
const fn::CPPType &type = info.type_of(i);
StringRef name = info.name_of(i);
if (name == "ID") {
int start_id = next_id_.fetch_add(size);
MutableSpan<int> ids = attributes.get<int>("ID");
for (int pindex : IndexRange(size)) {
ids[pindex] = start_id + pindex;
}
}
else if (name == "Hash") {
MutableSpan<int> hashes = attributes.get<int>("Hash");
RandomNumberGenerator rng(hash_seed_ ^ static_cast<uint32_t>(next_id_));
for (int pindex : IndexRange(size)) {
hashes[pindex] = static_cast<int>(rng.get_uint32());
}
}
else {
type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size);
}
}
return attributes;
}
} // namespace blender::sim

View File

@ -1,98 +0,0 @@
/*
* 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.
*/
#pragma once
#include "BLI_array.hh"
#include "BLI_vector.hh"
#include "FN_attributes_ref.hh"
#include <atomic>
#include <mutex>
namespace blender::sim {
class AttributesAllocator : NonCopyable, NonMovable {
private:
struct AttributesBlock {
Array<void *> buffers;
int size;
};
const fn::AttributesInfo &attributes_info_;
Vector<std::unique_ptr<AttributesBlock>> allocated_blocks_;
Vector<fn::MutableAttributesRef> allocated_attributes_;
int total_allocated_ = 0;
std::mutex mutex_;
public:
AttributesAllocator(const fn::AttributesInfo &attributes_info)
: attributes_info_(attributes_info)
{
}
~AttributesAllocator();
Span<fn::MutableAttributesRef> get_allocations() const
{
return allocated_attributes_;
}
int total_allocated() const
{
return total_allocated_;
}
const fn::AttributesInfo &attributes_info() const
{
return attributes_info_;
}
fn::MutableAttributesRef allocate_uninitialized(int size);
};
class ParticleAllocator : NonCopyable, NonMovable {
private:
AttributesAllocator attributes_allocator_;
std::atomic<int> next_id_;
uint32_t hash_seed_;
public:
ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed)
: attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed)
{
}
const fn::AttributesInfo &attributes_info() const
{
return attributes_allocator_.attributes_info();
}
Span<fn::MutableAttributesRef> get_allocations() const
{
return attributes_allocator_.get_allocations();
}
int total_allocated() const
{
return attributes_allocator_.total_allocated();
}
fn::MutableAttributesRef allocate(int size);
};
} // namespace blender::sim

View File

@ -1,166 +0,0 @@
/*
* 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 "particle_function.hh"
namespace blender::sim {
ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn,
const fn::MultiFunction *per_particle_fn,
Span<const ParticleFunctionInput *> global_inputs,
Span<const ParticleFunctionInput *> per_particle_inputs,
Span<bool> output_is_global)
: global_fn_(global_fn),
per_particle_fn_(per_particle_fn),
global_inputs_(global_inputs),
per_particle_inputs_(per_particle_inputs),
output_is_global_(output_is_global)
{
for (int i : output_is_global_.index_range()) {
if (output_is_global_[i]) {
int param_index = global_inputs_.size() + global_output_indices_.size();
fn::MFParamType param_type = global_fn_->param_type(param_index);
BLI_assert(param_type.is_output());
output_types_.append(param_type.data_type());
output_names_.append(global_fn_->param_name(param_index));
global_output_indices_.append(i);
}
else {
int param_index = per_particle_inputs_.size() + per_particle_output_indices_.size();
fn::MFParamType param_type = per_particle_fn_->param_type(param_index);
BLI_assert(param_type.is_output());
output_types_.append(param_type.data_type());
output_names_.append(per_particle_fn_->param_name(param_index));
per_particle_output_indices_.append(i);
}
}
}
ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
const SimulationSolveContext &solve_context,
const ParticleChunkContext &particles)
: particle_fn_(particle_fn),
solve_context_(solve_context),
particles_(particles),
mask_(particles_.index_mask),
outputs_(particle_fn_.output_types_.size(), nullptr)
{
global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map);
per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map);
}
ParticleFunctionEvaluator::~ParticleFunctionEvaluator()
{
for (int output_index : outputs_.index_range()) {
void *buffer = outputs_[output_index];
fn::MFDataType data_type = particle_fn_.output_types_[output_index];
BLI_assert(data_type.is_single()); /* For now. */
const fn::CPPType &type = data_type.single_type();
if (particle_fn_.output_is_global_[output_index]) {
type.destruct(buffer);
}
else {
type.destruct_indices(outputs_[0], mask_);
}
}
}
void ParticleFunctionEvaluator::compute()
{
BLI_assert(!is_computed_);
this->compute_globals();
this->compute_per_particle();
is_computed_ = true;
}
fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const
{
#ifdef DEBUG
if (expected_name != "") {
StringRef real_name = particle_fn_.output_names_[output_index];
BLI_assert(expected_name == real_name);
}
BLI_assert(is_computed_);
#endif
UNUSED_VARS_NDEBUG(expected_name);
const void *buffer = outputs_[output_index];
const fn::CPPType &type = particle_fn_.output_types_[output_index].single_type();
if (particle_fn_.output_is_global_[output_index]) {
return fn::GVSpan::FromSingleWithMaxSize(type, buffer);
}
return fn::GVSpan(fn::GSpan(type, buffer, mask_.min_array_size()));
}
void ParticleFunctionEvaluator::compute_globals()
{
if (particle_fn_.global_fn_ == nullptr) {
return;
}
fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size());
/* Add input parameters. */
ParticleFunctionInputContext input_context{solve_context_, particles_};
for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) {
input->add_input(input_context, params, resources_);
}
/* Add output parameters. */
for (int output_index : particle_fn_.global_output_indices_) {
fn::MFDataType data_type = particle_fn_.output_types_[output_index];
BLI_assert(data_type.is_single()); /* For now. */
const fn::CPPType &type = data_type.single_type();
void *buffer = resources_.linear_allocator().allocate(type.size(), type.alignment());
params.add_uninitialized_single_output(fn::GMutableSpan(type, buffer, 1));
outputs_[output_index] = buffer;
}
particle_fn_.global_fn_->call({0}, params, global_context_);
}
void ParticleFunctionEvaluator::compute_per_particle()
{
if (particle_fn_.per_particle_fn_ == nullptr) {
return;
}
fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size());
/* Add input parameters. */
ParticleFunctionInputContext input_context{solve_context_, particles_};
for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) {
input->add_input(input_context, params, resources_);
}
/* Add output parameters. */
for (int output_index : particle_fn_.per_particle_output_indices_) {
fn::MFDataType data_type = particle_fn_.output_types_[output_index];
BLI_assert(data_type.is_single()); /* For now. */
const fn::CPPType &type = data_type.single_type();
void *buffer = resources_.linear_allocator().allocate(type.size() * mask_.min_array_size(),
type.alignment());
params.add_uninitialized_single_output(fn::GMutableSpan(type, buffer, mask_.min_array_size()));
outputs_[output_index] = buffer;
}
particle_fn_.per_particle_fn_->call(mask_, params, global_context_);
}
} // namespace blender::sim

View File

@ -1,94 +0,0 @@
/*
* 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.
*/
#pragma once
#include "FN_attributes_ref.hh"
#include "FN_multi_function.hh"
#include "BLI_resource_collector.hh"
#include "simulation_solver.hh"
namespace blender::sim {
struct ParticleFunctionInputContext {
const SimulationSolveContext &solve_context;
const ParticleChunkContext &particles;
};
class ParticleFunctionInput {
public:
virtual ~ParticleFunctionInput() = default;
virtual void add_input(ParticleFunctionInputContext &context,
fn::MFParamsBuilder &params,
ResourceCollector &resources) const = 0;
};
class ParticleFunction {
private:
const fn::MultiFunction *global_fn_;
const fn::MultiFunction *per_particle_fn_;
Array<const ParticleFunctionInput *> global_inputs_;
Array<const ParticleFunctionInput *> per_particle_inputs_;
Array<bool> output_is_global_;
Vector<int> global_output_indices_;
Vector<int> per_particle_output_indices_;
Vector<fn::MFDataType> output_types_;
Vector<StringRefNull> output_names_;
friend class ParticleFunctionEvaluator;
public:
ParticleFunction(const fn::MultiFunction *global_fn,
const fn::MultiFunction *per_particle_fn,
Span<const ParticleFunctionInput *> global_inputs,
Span<const ParticleFunctionInput *> per_particle_inputs,
Span<bool> output_is_global);
};
class ParticleFunctionEvaluator {
private:
ResourceCollector resources_;
const ParticleFunction &particle_fn_;
const SimulationSolveContext &solve_context_;
const ParticleChunkContext &particles_;
IndexMask mask_;
fn::MFContextBuilder global_context_;
fn::MFContextBuilder per_particle_context_;
Vector<void *> outputs_;
bool is_computed_ = false;
public:
ParticleFunctionEvaluator(const ParticleFunction &particle_fn,
const SimulationSolveContext &solve_context,
const ParticleChunkContext &particles);
~ParticleFunctionEvaluator();
void compute();
fn::GVSpan get(int output_index, StringRef expected_name = "") const;
template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const
{
return this->get(output_index, expected_name).typed<T>();
}
private:
void compute_globals();
void compute_per_particle();
};
} // namespace blender::sim

View File

@ -1,362 +0,0 @@
/*
* 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 "particle_mesh_emitter.hh"
#include "BLI_float4x4.hh"
#include "BLI_rand.hh"
#include "BLI_vector_adaptor.hh"
#include "BKE_mesh_runtime.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
namespace blender::sim {
ParticleMeshEmitter::~ParticleMeshEmitter() = default;
struct EmitterSettings {
Object *object;
float rate;
};
static BLI_NOINLINE void compute_birth_times(float rate,
TimeInterval emit_interval,
ParticleMeshEmitterSimulationState &state,
Vector<float> &r_birth_times)
{
const float time_between_particles = 1.0f / rate;
int counter = 0;
while (true) {
counter++;
const float time_offset = counter * time_between_particles;
const float birth_time = state.last_birth_time + time_offset;
if (birth_time > emit_interval.stop()) {
break;
}
if (birth_time <= emit_interval.start()) {
continue;
}
r_birth_times.append(birth_time);
}
}
static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh)
{
const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh);
int amount = BKE_mesh_runtime_looptri_len(&mesh);
return Span(triangles, amount);
}
static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh,
Span<MLoopTri> triangles,
MutableSpan<float> r_areas)
{
assert_same_size(triangles, r_areas);
for (int i : triangles.index_range()) {
const MLoopTri &tri = triangles[i];
const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co;
const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co;
const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co;
const float area = area_tri_v3(v1, v2, v3);
r_areas[i] = area;
}
}
static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh,
Span<MLoopTri> triangles,
MutableSpan<float> r_weights)
{
assert_same_size(triangles, r_weights);
compute_triangle_areas(mesh, triangles, r_weights);
}
static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights,
MutableSpan<float> r_cumulative_weights)
{
BLI_assert(weights.size() + 1 == r_cumulative_weights.size());
r_cumulative_weights[0] = 0;
for (int i : weights.index_range()) {
r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i];
}
}
static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng,
int amount,
int start,
int one_after_end,
Span<float> cumulative_weights,
VectorAdaptor<int> &r_sampled_indices)
{
BLI_assert(start <= one_after_end);
const int size = one_after_end - start;
if (size == 0) {
BLI_assert(amount == 0);
}
else if (amount == 0) {
return;
}
else if (size == 1) {
r_sampled_indices.append_n_times(start, amount);
}
else {
const int middle = start + size / 2;
const float left_weight = cumulative_weights[middle] - cumulative_weights[start];
const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle];
BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f);
const float weight_sum = left_weight + right_weight;
BLI_assert(weight_sum > 0.0f);
const float left_factor = left_weight / weight_sum;
const float right_factor = right_weight / weight_sum;
int left_amount = amount * left_factor;
int right_amount = amount * right_factor;
if (left_amount + right_amount < amount) {
BLI_assert(left_amount + right_amount + 1 == amount);
const float weight_per_item = weight_sum / amount;
const float total_remaining_weight = weight_sum -
(left_amount + right_amount) * weight_per_item;
const float left_remaining_weight = left_weight - left_amount * weight_per_item;
const float left_remaining_factor = left_remaining_weight / total_remaining_weight;
if (rng.get_float() < left_remaining_factor) {
left_amount++;
}
else {
right_amount++;
}
}
sample_cumulative_distribution_recursive(
rng, left_amount, start, middle, cumulative_weights, r_sampled_indices);
sample_cumulative_distribution_recursive(
rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices);
}
}
static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng,
Span<float> cumulative_weights,
MutableSpan<int> r_samples)
{
VectorAdaptor<int> sampled_indices(r_samples);
sample_cumulative_distribution_recursive(rng,
r_samples.size(),
0,
cumulative_weights.size() - 1,
cumulative_weights,
sampled_indices);
BLI_assert(sampled_indices.is_full());
}
static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng,
Span<float> weights,
MutableSpan<int> r_samples)
{
Array<float> cumulative_weights(weights.size() + 1);
compute_cumulative_distribution(weights, cumulative_weights);
if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) {
/* All weights are zero. */
return false;
}
sample_cumulative_distribution(rng, cumulative_weights, r_samples);
return true;
}
static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng,
Mesh &mesh,
Span<MLoopTri> triangles,
Span<int> triangles_to_sample,
MutableSpan<float3> r_sampled_positions,
MutableSpan<float3> r_sampled_normals)
{
assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals);
MLoop *loops = mesh.mloop;
MVert *verts = mesh.mvert;
for (uint i : triangles_to_sample.index_range()) {
const uint triangle_index = triangles_to_sample[i];
const MLoopTri &triangle = triangles[triangle_index];
const float3 v1 = verts[loops[triangle.tri[0]].v].co;
const float3 v2 = verts[loops[triangle.tri[1]].v].co;
const float3 v3 = verts[loops[triangle.tri[2]].v].co;
const float3 bary_coords = rng.get_barycentric_coordinates();
float3 position;
interp_v3_v3v3v3(position, v1, v2, v3, bary_coords);
float3 normal;
normal_tri_v3(normal, v1, v2, v3);
r_sampled_positions[i] = position;
r_sampled_normals[i] = normal;
}
}
static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context,
EmitterSettings &settings,
ParticleMeshEmitterSimulationState &state,
Vector<float3> &r_positions,
Vector<float3> &r_velocities,
Vector<float> &r_birth_times)
{
if (settings.object == nullptr) {
return false;
}
if (settings.rate <= 0.000001f) {
return false;
}
if (settings.object->type != OB_MESH) {
return false;
}
Mesh &mesh = *static_cast<Mesh *>(settings.object->data);
if (mesh.totvert == 0) {
return false;
}
const float start_time = context.emit_interval.start();
const uint32_t seed = DefaultHash<StringRef>{}(state.head.name);
RandomNumberGenerator rng{*reinterpret_cast<const uint32_t *>(&start_time) ^ seed};
compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times);
const int particle_amount = r_birth_times.size();
if (particle_amount == 0) {
return false;
}
const float last_birth_time = r_birth_times.last();
rng.shuffle(r_birth_times.as_mutable_span());
Span<MLoopTri> triangles = get_mesh_triangles(mesh);
if (triangles.is_empty()) {
return false;
}
Array<float> triangle_weights(triangles.size());
compute_triangle_weights(mesh, triangles, triangle_weights);
Array<int> triangles_to_sample(particle_amount);
if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) {
return false;
}
r_positions.resize(particle_amount);
r_velocities.resize(particle_amount);
sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities);
if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) {
Array<float4x4> local_to_world_matrices(particle_amount);
context.solve_context.dependency_animations.get_object_transforms(
*settings.object, r_birth_times, local_to_world_matrices);
for (int i : IndexRange(particle_amount)) {
const float4x4 &position_to_world = local_to_world_matrices[i];
const float4x4 normal_to_world = position_to_world.inverted_transposed_affine();
r_positions[i] = position_to_world * r_positions[i];
r_velocities[i] = normal_to_world * r_velocities[i];
}
}
else {
const float4x4 position_to_world = settings.object->obmat;
const float4x4 normal_to_world = position_to_world.inverted_transposed_affine();
for (int i : IndexRange(particle_amount)) {
r_positions[i] = position_to_world * r_positions[i];
r_velocities[i] = normal_to_world * r_velocities[i];
}
}
for (int i : IndexRange(particle_amount)) {
r_velocities[i].normalize();
}
state.last_birth_time = last_birth_time;
return true;
}
static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn,
ParticleEmitterContext &context)
{
EmitterSettings parameters;
fn::MFContextBuilder mf_context;
mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map);
fn::MFParamsBuilder mf_params{inputs_fn, 1};
bke::PersistentObjectHandle object_handle;
mf_params.add_uninitialized_single_output(&object_handle, "Object");
mf_params.add_uninitialized_single_output(&parameters.rate, "Rate");
inputs_fn.call(IndexRange(1), mf_params, mf_context);
parameters.object = context.solve_context.handle_map.lookup(object_handle);
return parameters;
}
void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const
{
auto *state = context.lookup_state<ParticleMeshEmitterSimulationState>(own_state_name_);
if (state == nullptr) {
return;
}
EmitterSettings settings = compute_settings(inputs_fn_, context);
Vector<float3> new_positions;
Vector<float3> new_velocities;
Vector<float> new_birth_times;
if (!compute_new_particle_attributes(
context, settings, *state, new_positions, new_velocities, new_birth_times)) {
return;
}
for (StringRef name : particle_names_) {
ParticleAllocator *allocator = context.try_get_particle_allocator(name);
if (allocator == nullptr) {
continue;
}
int amount = new_positions.size();
fn::MutableAttributesRef attributes = allocator->allocate(amount);
attributes.get<float3>("Position").copy_from(new_positions);
attributes.get<float3>("Velocity").copy_from(new_velocities);
attributes.get<float>("Birth Time").copy_from(new_birth_times);
if (action_ != nullptr) {
ParticleChunkContext particles{
*context.solve_context.state_map.lookup<ParticleSimulationState>(name),
IndexRange(amount),
attributes,
nullptr};
ParticleActionContext action_context{context.solve_context, particles};
action_->execute(action_context);
}
}
}
} // namespace blender::sim

View File

@ -1,49 +0,0 @@
/*
* 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.
*/
#pragma once
#include "simulation_solver_influences.hh"
#include "FN_multi_function.hh"
namespace blender::sim {
class ParticleMeshEmitter final : public ParticleEmitter {
private:
std::string own_state_name_;
Array<std::string> particle_names_;
const fn::MultiFunction &inputs_fn_;
const ParticleAction *action_;
public:
ParticleMeshEmitter(std::string own_state_name,
Array<std::string> particle_names,
const fn::MultiFunction &inputs_fn,
const ParticleAction *action)
: own_state_name_(std::move(own_state_name)),
particle_names_(particle_names),
inputs_fn_(inputs_fn),
action_(action)
{
}
~ParticleMeshEmitter();
void emit(ParticleEmitterContext &context) const override;
};
} // namespace blender::sim

View File

@ -1,907 +0,0 @@
/*
* 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 "simulation_collect_influences.hh"
#include "particle_function.hh"
#include "particle_mesh_emitter.hh"
#include "FN_attributes_ref.hh"
#include "FN_multi_function_network_evaluation.hh"
#include "FN_multi_function_network_optimization.hh"
#include "NOD_node_tree_multi_function.hh"
#include "DEG_depsgraph_query.h"
#include "BLI_hash.h"
#include "BLI_rand.hh"
#include "BLI_set.hh"
namespace blender::sim {
using fn::GVSpan;
using fn::MFContextBuilder;
using fn::MFDataType;
using fn::MFDummyNode;
using fn::MFFunctionNode;
using fn::MFInputSocket;
using fn::MFNetwork;
using fn::MFNetworkEvaluator;
using fn::MFNode;
using fn::MFOutputSocket;
using fn::MFParamsBuilder;
using fn::MFParamType;
using fn::MultiFunction;
using fn::VSpan;
using nodes::DerivedNodeTree;
using nodes::DInputSocket;
using nodes::DNode;
using nodes::DOutputSocket;
using nodes::DParentNode;
using nodes::MFNetworkTreeMap;
using nodes::NodeTreeRefMap;
struct DummyDataSources {
Map<const MFOutputSocket *, std::string> particle_attributes;
Set<const MFOutputSocket *> simulation_time;
Set<const MFOutputSocket *> scene_time;
};
extern "C" {
void WM_clipboard_text_set(const char *buf, bool selection);
}
static std::string dnode_to_path(const DNode &dnode)
{
std::string path;
for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) {
path = parent->node_ref().name() + "/" + path;
}
path = path + dnode.name();
return path;
}
struct CollectContext : NonCopyable, NonMovable {
SimulationInfluences &influences;
RequiredStates &required_states;
ResourceCollector &resources;
MFNetworkTreeMap &network_map;
MFNetwork &network;
const DerivedNodeTree &tree;
DummyDataSources data_sources;
Span<const DNode *> particle_simulation_nodes;
Map<const DNode *, std::string> node_paths;
CollectContext(SimulationInfluences &influences,
RequiredStates &required_states,
ResourceCollector &resources,
MFNetworkTreeMap &network_map)
: influences(influences),
required_states(required_states),
resources(resources),
network_map(network_map),
network(network_map.network()),
tree(network_map.tree())
{
particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation");
}
};
static const ParticleAction *create_particle_action(CollectContext &context,
const DOutputSocket &dsocket,
Span<StringRefNull> particle_names);
static const ParticleAction *create_particle_action(CollectContext &context,
const DInputSocket &dsocket,
Span<StringRefNull> particle_names)
{
BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW);
if (dsocket.linked_sockets().size() != 1) {
return nullptr;
}
return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names);
}
static StringRefNull get_identifier(CollectContext &context, const DNode &dnode)
{
return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); });
}
static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname)
{
return context.tree.nodes_by_type(idname);
}
static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context,
const DOutputSocket &output_socket)
{
VectorSet<StringRefNull> names;
for (const DInputSocket *target_socket : output_socket.linked_sockets()) {
if (target_socket->node().idname() == "SimulationNodeParticleSimulation") {
names.add(get_identifier(context, target_socket->node()));
}
}
return names.as_span();
}
/* Returns true on success. */
static bool compute_global_inputs(MFNetworkTreeMap &network_map,
ResourceCollector &resources,
Span<const MFInputSocket *> sockets,
MutableSpan<GMutableSpan> r_results)
{
int amount = sockets.size();
if (amount == 0) {
return true;
}
if (network_map.network().have_dummy_or_unlinked_dependencies(sockets)) {
return false;
}
MFNetworkEvaluator network_fn{{}, sockets};
MFParamsBuilder params{network_fn, 1};
for (int param_index : network_fn.param_indices()) {
MFParamType param_type = network_fn.param_type(param_index);
BLI_assert(param_type.category() == MFParamType::Category::SingleOutput); /* For now. */
const CPPType &type = param_type.data_type().single_type();
void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment());
resources.add(buffer, type.destruct_cb(), AT);
GMutableSpan span{type, buffer, 1};
r_results[param_index] = span;
params.add_uninitialized_single_output(span);
}
MFContextBuilder context;
network_fn.call(IndexRange(1), params, context);
return true;
}
static std::optional<Array<std::string>> compute_global_string_inputs(
MFNetworkTreeMap &network_map, Span<const MFInputSocket *> sockets)
{
ResourceCollector local_resources;
Array<GMutableSpan> computed_values(sockets.size(), NoInitialization());
if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) {
return {};
}
Array<std::string> strings(sockets.size());
for (int i : sockets.index_range()) {
strings[i] = std::move(computed_values[i].typed<std::string>()[0]);
}
return strings;
}
/**
* This will find all the particle attribute input nodes. Then it will compute the attribute names
* by evaluating the network (those names should not depend on per particle data). In the end,
* input nodes that access the same attribute are combined.
*/
static void prepare_particle_attribute_nodes(CollectContext &context)
{
Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute");
Vector<MFInputSocket *> name_sockets;
for (const DNode *dnode : attribute_dnodes) {
MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0));
name_sockets.append(&name_socket);
}
std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(
context.network_map, name_sockets);
if (!attribute_names.has_value()) {
return;
}
MultiValueMap<std::pair<std::string, MFDataType>, MFNode *> attribute_nodes_by_name_and_type;
for (int i : attribute_names->index_range()) {
attribute_nodes_by_name_and_type.add(
{(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()},
&name_sockets[i]->node());
}
Map<const MFOutputSocket *, std::string> attribute_inputs;
for (auto item : attribute_nodes_by_name_and_type.items()) {
StringRef attribute_name = item.key.first;
MFDataType data_type = item.key.second;
Span<MFNode *> nodes = item.value;
MFOutputSocket &new_attribute_socket = context.network.add_input(
"Attribute '" + attribute_name + "'", data_type);
for (MFNode *node : nodes) {
context.network.relink(node->output(0), new_attribute_socket);
}
context.network.remove(nodes);
context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name);
}
}
static void prepare_time_input_nodes(CollectContext &context)
{
Span<const DNode *> time_input_dnodes = nodes_by_type(context, "SimulationNodeTime");
Vector<const DNode *> simulation_time_inputs;
Vector<const DNode *> scene_time_inputs;
for (const DNode *dnode : time_input_dnodes) {
NodeSimInputTimeType type = (NodeSimInputTimeType)dnode->node_ref().bnode()->custom1;
switch (type) {
case NODE_SIM_INPUT_SIMULATION_TIME: {
simulation_time_inputs.append(dnode);
break;
}
case NODE_SIM_INPUT_SCENE_TIME: {
scene_time_inputs.append(dnode);
break;
}
}
}
if (simulation_time_inputs.size() > 0) {
MFOutputSocket &new_socket = context.network.add_input("Simulation Time",
MFDataType::ForSingle<float>());
for (const DNode *dnode : simulation_time_inputs) {
MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0));
context.network.relink(old_socket, new_socket);
context.network.remove(old_socket.node());
}
context.data_sources.simulation_time.add(&new_socket);
}
if (scene_time_inputs.size() > 0) {
MFOutputSocket &new_socket = context.network.add_input("Scene Time",
MFDataType::ForSingle<float>());
for (const DNode *dnode : scene_time_inputs) {
MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0));
context.network.relink(old_socket, new_socket);
context.network.remove(old_socket.node());
}
context.data_sources.scene_time.add(&new_socket);
}
}
class ParticleAttributeInput : public ParticleFunctionInput {
private:
std::string attribute_name_;
const CPPType &attribute_type_;
public:
ParticleAttributeInput(std::string attribute_name, const CPPType &attribute_type)
: attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type)
{
}
void add_input(ParticleFunctionInputContext &context,
MFParamsBuilder &params,
ResourceCollector &UNUSED(resources)) const override
{
std::optional<GSpan> span = context.particles.attributes.try_get(attribute_name_,
attribute_type_);
if (span.has_value()) {
params.add_readonly_single_input(*span);
}
else {
params.add_readonly_single_input(GVSpan::FromDefault(attribute_type_));
}
}
};
class SceneTimeInput : public ParticleFunctionInput {
void add_input(ParticleFunctionInputContext &context,
MFParamsBuilder &params,
ResourceCollector &resources) const override
{
const float time = DEG_get_ctime(&context.solve_context.depsgraph);
float *time_ptr = &resources.construct<float>(AT, time);
params.add_readonly_single_input(time_ptr);
}
};
class SimulationTimeInput : public ParticleFunctionInput {
void add_input(ParticleFunctionInputContext &context,
MFParamsBuilder &params,
ResourceCollector &resources) const override
{
/* TODO: Vary this per particle. */
const float time = context.solve_context.solve_interval.stop();
float *time_ptr = &resources.construct<float>(AT, time);
params.add_readonly_single_input(time_ptr);
}
};
static const ParticleFunction *create_particle_function_for_inputs(
CollectContext &context, Span<const MFInputSocket *> sockets_to_compute)
{
BLI_assert(sockets_to_compute.size() >= 1);
const MFNetwork &network = sockets_to_compute[0]->node().network();
VectorSet<const MFOutputSocket *> dummy_deps;
VectorSet<const MFInputSocket *> unlinked_input_deps;
network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps);
BLI_assert(unlinked_input_deps.size() == 0);
Vector<const ParticleFunctionInput *> per_particle_inputs;
for (const MFOutputSocket *socket : dummy_deps) {
if (context.data_sources.particle_attributes.contains(socket)) {
const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr(
socket);
if (attribute_name == nullptr) {
return nullptr;
}
per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>(
AT, *attribute_name, socket->data_type().single_type()));
}
else if (context.data_sources.scene_time.contains(socket)) {
per_particle_inputs.append(&context.resources.construct<SceneTimeInput>(AT));
}
else if (context.data_sources.simulation_time.contains(socket)) {
per_particle_inputs.append(&context.resources.construct<SimulationTimeInput>(AT));
}
}
const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>(
AT, dummy_deps.as_span(), sockets_to_compute);
Array<bool> output_is_global(sockets_to_compute.size(), false);
const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>(
AT,
nullptr,
&per_particle_fn,
Span<const ParticleFunctionInput *>(),
per_particle_inputs.as_span(),
output_is_global.as_span());
return &particle_fn;
}
static const ParticleFunction *create_particle_function_for_inputs(
CollectContext &context, Span<const DInputSocket *> dsockets_to_compute)
{
Vector<const MFInputSocket *> sockets_to_compute;
for (const DInputSocket *dsocket : dsockets_to_compute) {
const MFInputSocket &socket = context.network_map.lookup_dummy(*dsocket);
sockets_to_compute.append(&socket);
}
return create_particle_function_for_inputs(context, sockets_to_compute);
}
class ParticleFunctionForce : public ParticleForce {
private:
const ParticleFunction &particle_fn_;
public:
ParticleFunctionForce(const ParticleFunction &particle_fn) : particle_fn_(particle_fn)
{
}
void add_force(ParticleForceContext &context) const override
{
IndexMask mask = context.particles.index_mask;
MutableSpan<float3> r_combined_force = context.force_dst;
ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles};
evaluator.compute();
VSpan<float3> forces = evaluator.get<float3>(0, "Force");
for (int64_t i : mask) {
r_combined_force[i] += forces[i];
}
}
};
static void create_forces_for_particle_simulation(CollectContext &context,
const DNode &simulation_node)
{
Vector<const ParticleForce *> forces;
for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) {
const DNode &origin_node = origin_socket->node();
if (origin_node.idname() != "SimulationNodeForce") {
continue;
}
const ParticleFunction *particle_fn = create_particle_function_for_inputs(
context, {&origin_node.input(0, "Force")});
if (particle_fn == nullptr) {
continue;
}
const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT,
*particle_fn);
forces.append(&force);
}
StringRef particle_name = get_identifier(context, simulation_node);
context.influences.particle_forces.add_multiple_as(particle_name, forces);
}
static void collect_forces(CollectContext &context)
{
for (const DNode *dnode : context.particle_simulation_nodes) {
create_forces_for_particle_simulation(context, *dnode);
}
}
static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode)
{
Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0));
if (names.size() == 0) {
return nullptr;
}
Array<const MFInputSocket *> input_sockets{2};
for (int i : input_sockets.index_range()) {
input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i));
}
if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) {
return nullptr;
}
MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>(
AT, Span<const MFOutputSocket *>(), input_sockets.as_span());
const ParticleAction *birth_action = create_particle_action(
context, dnode.input(2, "Execute"), names);
StringRefNull own_state_name = get_identifier(context, dnode);
context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER);
ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>(
AT, own_state_name, names.as_span(), inputs_fn, birth_action);
return &emitter;
}
static void collect_emitters(CollectContext &context)
{
for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) {
ParticleEmitter *emitter = create_particle_emitter(context, *dnode);
if (emitter != nullptr) {
context.influences.particle_emitters.append(emitter);
}
}
}
static void collect_birth_events(CollectContext &context)
{
for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) {
const DInputSocket &execute_input = event_dnode->input(0);
if (execute_input.linked_sockets().size() != 1) {
continue;
}
Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
event_dnode->output(0));
const DOutputSocket &execute_source = *execute_input.linked_sockets()[0];
const ParticleAction *action = create_particle_action(context, execute_source, particle_names);
if (action == nullptr) {
continue;
}
for (StringRefNull particle_name : particle_names) {
context.influences.particle_birth_actions.add_as(particle_name, action);
}
}
}
static void collect_time_step_events(CollectContext &context)
{
for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) {
const DInputSocket &execute_input = event_dnode->input(0);
Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
event_dnode->output(0));
const ParticleAction *action = create_particle_action(context, execute_input, particle_names);
if (action == nullptr) {
continue;
}
NodeSimParticleTimeStepEventType type =
(NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1;
if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) {
for (StringRefNull particle_name : particle_names) {
context.influences.particle_time_step_begin_actions.add_as(particle_name, action);
}
}
else {
for (StringRefNull particle_name : particle_names) {
context.influences.particle_time_step_end_actions.add_as(particle_name, action);
}
}
}
}
class SequenceParticleAction : public ParticleAction {
private:
Vector<const ParticleAction *> actions_;
public:
SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions))
{
}
void execute(ParticleActionContext &context) const override
{
for (const ParticleAction *action : actions_) {
action->execute(context);
}
}
};
class SetParticleAttributeAction : public ParticleAction {
private:
std::string attribute_name_;
const CPPType &cpp_type_;
const ParticleFunction &inputs_fn_;
public:
SetParticleAttributeAction(std::string attribute_name,
const CPPType &cpp_type,
const ParticleFunction &inputs_fn)
: attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn)
{
}
void execute(ParticleActionContext &context) const override
{
std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get(
attribute_name_, cpp_type_);
if (!attribute_array.has_value()) {
return;
}
ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
evaluator.compute();
GVSpan values = evaluator.get(0);
if (values.is_single_element()) {
cpp_type_.fill_initialized_indices(
values.as_single_element(), attribute_array->data(), context.particles.index_mask);
}
else {
GSpan value_array = values.as_full_array();
cpp_type_.copy_to_initialized_indices(
value_array.data(), attribute_array->data(), context.particles.index_mask);
}
if (attribute_name_ == "Velocity") {
context.particles.update_diffs_after_velocity_change();
}
}
};
static const ParticleAction *concatenate_actions(CollectContext &context,
Span<const ParticleAction *> actions)
{
Vector<const ParticleAction *> non_null_actions;
for (const ParticleAction *action : actions) {
if (action != nullptr) {
non_null_actions.append(action);
}
}
if (non_null_actions.size() == 0) {
return nullptr;
}
if (non_null_actions.size() == 1) {
return non_null_actions[0];
}
return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions));
}
static const ParticleAction *create_set_particle_attribute_action(
CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
const ParticleAction *previous_action = create_particle_action(
context, dnode.input(0), particle_names);
MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1));
MFInputSocket &value_socket = name_socket.node().input(1);
std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map,
{&name_socket});
if (!names.has_value()) {
return previous_action;
}
std::string attribute_name = (*names)[0];
if (attribute_name.empty()) {
return previous_action;
}
const CPPType &attribute_type = value_socket.data_type().single_type();
const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context,
{&value_socket});
if (inputs_fn == nullptr) {
return previous_action;
}
for (StringRef particle_name : particle_names) {
context.influences.particle_attributes_builder.lookup_as(particle_name)
->add(attribute_name, attribute_type);
}
ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>(
AT, attribute_name, attribute_type, *inputs_fn);
return concatenate_actions(context, {previous_action, &this_action});
}
class ParticleConditionAction : public ParticleAction {
private:
const ParticleFunction &inputs_fn_;
const ParticleAction *action_true_;
const ParticleAction *action_false_;
public:
ParticleConditionAction(const ParticleFunction &inputs_fn,
const ParticleAction *action_true,
const ParticleAction *action_false)
: inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false)
{
}
void execute(ParticleActionContext &context) const override
{
ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
evaluator.compute();
VSpan<bool> conditions = evaluator.get<bool>(0, "Condition");
if (conditions.is_single_element()) {
const bool condition = conditions.as_single_element();
if (condition) {
if (action_true_ != nullptr) {
action_true_->execute(context);
}
}
else {
if (action_false_ != nullptr) {
action_false_->execute(context);
}
}
}
else {
Span<bool> conditions_array = conditions.as_full_array();
Vector<int64_t> true_indices;
Vector<int64_t> false_indices;
for (int i : context.particles.index_mask) {
if (conditions_array[i]) {
true_indices.append(i);
}
else {
false_indices.append(i);
}
}
if (action_true_ != nullptr) {
ParticleChunkContext chunk_context{context.particles.state,
true_indices.as_span(),
context.particles.attributes,
context.particles.integration};
ParticleActionContext action_context{context.solve_context, chunk_context};
action_true_->execute(action_context);
}
if (action_false_ != nullptr) {
ParticleChunkContext chunk_context{context.particles.state,
false_indices.as_span(),
context.particles.attributes,
context.particles.integration};
ParticleActionContext action_context{context.solve_context, chunk_context};
action_false_->execute(action_context);
}
}
}
};
static const ParticleAction *create_particle_condition_action(CollectContext &context,
const DOutputSocket &dsocket,
Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
const ParticleFunction *inputs_fn = create_particle_function_for_inputs(
context, {&dnode.input(0, "Condition")});
if (inputs_fn == nullptr) {
return nullptr;
}
const ParticleAction *true_action = create_particle_action(
context, dnode.input(1), particle_names);
const ParticleAction *false_action = create_particle_action(
context, dnode.input(2), particle_names);
if (true_action == nullptr && false_action == nullptr) {
return nullptr;
}
return &context.resources.construct<ParticleConditionAction>(
AT, *inputs_fn, true_action, false_action);
}
class KillParticleAction : public ParticleAction {
public:
void execute(ParticleActionContext &context) const override
{
MutableSpan<int> dead_states = context.particles.attributes.get<int>("Dead");
for (int i : context.particles.index_mask) {
dead_states[i] = true;
}
}
};
static const ParticleAction *create_particle_action(CollectContext &context,
const DOutputSocket &dsocket,
Span<StringRefNull> particle_names)
{
const DNode &dnode = dsocket.node();
StringRef idname = dnode.idname();
if (idname == "SimulationNodeSetParticleAttribute") {
return create_set_particle_attribute_action(context, dsocket, particle_names);
}
if (idname == "SimulationNodeExecuteCondition") {
return create_particle_condition_action(context, dsocket, particle_names);
}
if (idname == "SimulationNodeKillParticle") {
return &context.resources.construct<KillParticleAction>(AT);
}
return nullptr;
}
static void initialize_particle_attribute_builders(CollectContext &context)
{
for (const DNode *dnode : context.particle_simulation_nodes) {
StringRef name = get_identifier(context, *dnode);
AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>(
AT);
attributes_builder.add<float3>("Position", {0, 0, 0});
attributes_builder.add<float3>("Velocity", {0, 0, 0});
attributes_builder.add<int>("ID", 0);
/* TODO: Use bool property, but need to add CD_PROP_BOOL first. */
attributes_builder.add<int>("Dead", 0);
/* TODO: Use uint32_t, but we don't have a corresponding custom property type. */
attributes_builder.add<int>("Hash", 0);
attributes_builder.add<float>("Birth Time", 0.0f);
attributes_builder.add<float>("Radius", 0.02f);
context.influences.particle_attributes_builder.add_new(name, &attributes_builder);
}
}
static void optimize_function_network(CollectContext &context)
{
fn::mf_network_optimization::constant_folding(context.network, context.resources);
fn::mf_network_optimization::common_subnetwork_elimination(context.network);
fn::mf_network_optimization::dead_node_removal(context.network);
// WM_clipboard_text_set(network.to_dot().c_str(), false);
}
class AgeReachedEvent : public ParticleEvent {
private:
std::string attribute_name_;
const ParticleFunction &inputs_fn_;
const ParticleAction &action_;
public:
AgeReachedEvent(std::string attribute_name,
const ParticleFunction &inputs_fn,
const ParticleAction &action)
: attribute_name_(std::move(attribute_name)), inputs_fn_(inputs_fn), action_(action)
{
}
void filter(ParticleEventFilterContext &context) const override
{
Span<float> birth_times = context.particles.attributes.get<float>("Birth Time");
std::optional<Span<int>> has_been_triggered = context.particles.attributes.try_get<int>(
attribute_name_);
if (!has_been_triggered.has_value()) {
return;
}
ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles};
evaluator.compute();
VSpan<float> trigger_ages = evaluator.get<float>(0, "Age");
const float end_time = context.particles.integration->end_time;
for (int i : context.particles.index_mask) {
if ((*has_been_triggered)[i]) {
continue;
}
const float trigger_age = trigger_ages[i];
const float birth_time = birth_times[i];
const float trigger_time = birth_time + trigger_age;
if (trigger_time > end_time) {
continue;
}
const float duration = context.particles.integration->durations[i];
TimeInterval interval(end_time - duration, duration);
const float time_factor = interval.safe_factor_at_time(trigger_time);
context.factor_dst[i] = std::max<float>(0.0f, time_factor);
}
}
void execute(ParticleActionContext &context) const override
{
MutableSpan<int> has_been_triggered = context.particles.attributes.get<int>(attribute_name_);
for (int i : context.particles.index_mask) {
has_been_triggered[i] = 1;
}
action_.execute(context);
}
};
static void collect_age_reached_events(CollectContext &context)
{
for (const DNode *dnode : nodes_by_type(context, "SimulationNodeAgeReachedEvent")) {
const DInputSocket &age_input = dnode->input(0, "Age");
const DInputSocket &execute_input = dnode->input(1, "Execute");
Array<StringRefNull> particle_names = find_linked_particle_simulations(context,
dnode->output(0));
const ParticleAction *action = create_particle_action(context, execute_input, particle_names);
if (action == nullptr) {
continue;
}
const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, {&age_input});
if (inputs_fn == nullptr) {
continue;
}
std::string attribute_name = get_identifier(context, *dnode);
const ParticleEvent &event = context.resources.construct<AgeReachedEvent>(
AT, attribute_name, *inputs_fn, *action);
for (StringRefNull particle_name : particle_names) {
const bool added_attribute = context.influences.particle_attributes_builder
.lookup_as(particle_name)
->add<int>(attribute_name, 0);
if (added_attribute) {
context.influences.particle_events.add_as(particle_name, &event);
}
}
}
}
void collect_simulation_influences(Simulation &simulation,
ResourceCollector &resources,
SimulationInfluences &r_influences,
RequiredStates &r_required_states)
{
NodeTreeRefMap tree_refs;
const DerivedNodeTree tree{simulation.nodetree, tree_refs};
MFNetwork &network = resources.construct<MFNetwork>(AT);
MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources);
CollectContext context{r_influences, r_required_states, resources, network_map};
initialize_particle_attribute_builders(context);
prepare_particle_attribute_nodes(context);
prepare_time_input_nodes(context);
collect_forces(context);
collect_emitters(context);
collect_birth_events(context);
collect_time_step_events(context);
collect_age_reached_events(context);
optimize_function_network(context);
for (const DNode *dnode : context.particle_simulation_nodes) {
r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION);
}
}
} // namespace blender::sim

View File

@ -1,65 +0,0 @@
/*
* 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.
*/
#pragma once
#include "NOD_derived_node_tree.hh"
#include "BLI_resource_collector.hh"
#include "simulation_solver_influences.hh"
namespace blender::sim {
class RequiredStates {
private:
Map<std::string, const char *> state_type_by_state_name_;
public:
void add(std::string state_name, const char *state_type)
{
BLI_assert(state_type != nullptr);
const char *type_name = state_type_by_state_name_.lookup_default(state_name, nullptr);
if (type_name != nullptr) {
if (!STREQ(state_type, type_name)) {
std::cout << "Warning: Tried to have two different states with the same name.\n";
std::cout << " Name: " << state_name << "\n";
std::cout << " Type 1: " << state_type << "\n";
std::cout << " Type 2: " << type_name << "\n";
}
return;
}
state_type_by_state_name_.add(std::move(state_name), state_type);
}
const Map<std::string, const char *> &states() const
{
return state_type_by_state_name_;
}
bool is_required(StringRef state_name, StringRef state_type) const
{
return state_type_by_state_name_.lookup_default_as(state_name, "") == state_type;
}
};
void collect_simulation_influences(Simulation &simulation,
ResourceCollector &resources,
SimulationInfluences &r_influences,
RequiredStates &r_required_states);
} // namespace blender::sim

View File

@ -1,522 +0,0 @@
/*
* 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 "simulation_solver.hh"
#include "BKE_customdata.h"
#include "BKE_persistent_data_handle.hh"
#include "BLI_rand.hh"
#include "BLI_set.hh"
#include "DEG_depsgraph_query.h"
namespace blender::sim {
static CustomDataType cpp_to_custom_data_type(const CPPType &type)
{
if (type.is<float3>()) {
return CD_PROP_FLOAT3;
}
if (type.is<float>()) {
return CD_PROP_FLOAT;
}
if (type.is<int32_t>()) {
return CD_PROP_INT32;
}
BLI_assert(false);
return CD_PROP_FLOAT;
}
static const CPPType &custom_to_cpp_data_type(CustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT3:
return CPPType::get<float3>();
case CD_PROP_FLOAT:
return CPPType::get<float>();
case CD_PROP_INT32:
return CPPType::get<int32_t>();
default:
BLI_assert(false);
return CPPType::get<float>();
}
}
class CustomDataAttributesRef {
private:
Array<void *> buffers_;
int64_t size_;
const AttributesInfo &info_;
public:
CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info)
: buffers_(info.size(), nullptr), size_(size), info_(info)
{
for (int attribute_index : info.index_range()) {
StringRefNull name = info.name_of(attribute_index);
const CPPType &cpp_type = info.type_of(attribute_index);
CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str());
buffers_[attribute_index] = data;
}
}
operator MutableAttributesRef()
{
return MutableAttributesRef(info_, buffers_, size_);
}
operator AttributesRef() const
{
return AttributesRef(info_, buffers_, size_);
}
};
static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info)
{
bool found_layer_to_remove;
do {
found_layer_to_remove = false;
for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) {
CustomDataLayer *layer = &state->attributes.layers[layer_index];
BLI_assert(layer->name != nullptr);
const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type);
StringRefNull name = layer->name;
if (!info.has_attribute(name, cpp_type)) {
found_layer_to_remove = true;
CustomData_free_layer(&state->attributes, layer->type, state->tot_particles, layer_index);
break;
}
}
} while (found_layer_to_remove);
for (int attribute_index : info.index_range()) {
StringRefNull attribute_name = info.name_of(attribute_index);
const CPPType &cpp_type = info.type_of(attribute_index);
CustomDataType custom_type = cpp_to_custom_data_type(cpp_type);
if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) ==
nullptr) {
void *data = CustomData_add_layer_named(&state->attributes,
custom_type,
CD_CALLOC,
nullptr,
state->tot_particles,
attribute_name.c_str());
cpp_type.fill_uninitialized(info.default_of(attribute_index), data, state->tot_particles);
}
}
}
BLI_NOINLINE static void apply_remaining_diffs(ParticleChunkContext &context)
{
BLI_assert(context.integration != nullptr);
MutableSpan<float3> positions = context.attributes.get<float3>("Position");
MutableSpan<float3> velocities = context.attributes.get<float3>("Velocity");
for (int i : context.index_mask) {
positions[i] += context.integration->position_diffs[i];
velocities[i] += context.integration->velocity_diffs[i];
}
}
BLI_NOINLINE static void find_next_event_per_particle(
SimulationSolveContext &solve_context,
ParticleChunkContext &particles,
Span<const ParticleEvent *> events,
MutableSpan<int> r_next_event_indices,
MutableSpan<float> r_time_factors_to_next_event)
{
r_next_event_indices.fill_indices(particles.index_mask, -1);
r_time_factors_to_next_event.fill_indices(particles.index_mask, 1.0f);
Array<float> time_factors(particles.index_mask.min_array_size());
for (int event_index : events.index_range()) {
time_factors.fill(-1.0f);
ParticleEventFilterContext event_context{solve_context, particles, time_factors};
const ParticleEvent &event = *events[event_index];
event.filter(event_context);
for (int i : particles.index_mask) {
const float time_factor = time_factors[i];
const float previously_smallest_time_factor = r_time_factors_to_next_event[i];
if (time_factor >= 0.0f && time_factor <= previously_smallest_time_factor) {
r_time_factors_to_next_event[i] = time_factor;
r_next_event_indices[i] = event_index;
}
}
}
}
BLI_NOINLINE static void forward_particles_to_next_event_or_end(
ParticleChunkContext &particles, Span<float> time_factors_to_next_event)
{
MutableSpan<float3> positions = particles.attributes.get<float3>("Position");
MutableSpan<float3> velocities = particles.attributes.get<float3>("Velocity");
MutableSpan<float3> position_diffs = particles.integration->position_diffs;
MutableSpan<float3> velocity_diffs = particles.integration->velocity_diffs;
MutableSpan<float> durations = particles.integration->durations;
for (int i : particles.index_mask) {
const float time_factor = time_factors_to_next_event[i];
positions[i] += position_diffs[i] * time_factor;
velocities[i] += velocity_diffs[i] * time_factor;
const float remaining_time_factor = 1.0f - time_factor;
position_diffs[i] *= remaining_time_factor;
velocity_diffs[i] *= remaining_time_factor;
durations[i] *= remaining_time_factor;
}
}
BLI_NOINLINE static void group_particles_by_event(
IndexMask mask,
Span<int> next_event_indices,
MutableSpan<Vector<int64_t>> r_particles_per_event)
{
for (int i : mask) {
int event_index = next_event_indices[i];
if (event_index >= 0) {
r_particles_per_event[event_index].append(i);
}
}
}
BLI_NOINLINE static void execute_events(SimulationSolveContext &solve_context,
ParticleChunkContext &all_particles,
Span<const ParticleEvent *> events,
Span<Vector<int64_t>> particles_per_event)
{
for (int event_index : events.index_range()) {
Span<int64_t> pindices = particles_per_event[event_index];
if (pindices.is_empty()) {
continue;
}
const ParticleEvent &event = *events[event_index];
ParticleChunkContext particles{
all_particles.state, pindices, all_particles.attributes, all_particles.integration};
ParticleActionContext action_context{solve_context, particles};
event.execute(action_context);
}
}
BLI_NOINLINE static void find_unfinished_particles(IndexMask index_mask,
Span<float> time_factors_to_next_event,
Vector<int64_t> &r_unfinished_pindices)
{
for (int i : index_mask) {
float time_factor = time_factors_to_next_event[i];
if (time_factor < 1.0f) {
r_unfinished_pindices.append(i);
}
}
}
BLI_NOINLINE static void simulate_to_next_event(SimulationSolveContext &solve_context,
ParticleChunkContext &particles,
Span<const ParticleEvent *> events,
Vector<int64_t> &r_unfinished_pindices)
{
int array_size = particles.index_mask.min_array_size();
Array<int> next_event_indices(array_size);
Array<float> time_factors_to_next_event(array_size);
find_next_event_per_particle(
solve_context, particles, events, next_event_indices, time_factors_to_next_event);
forward_particles_to_next_event_or_end(particles, time_factors_to_next_event);
Array<Vector<int64_t>> particles_per_event(events.size());
group_particles_by_event(particles.index_mask, next_event_indices, particles_per_event);
execute_events(solve_context, particles, events, particles_per_event);
find_unfinished_particles(
particles.index_mask, time_factors_to_next_event, r_unfinished_pindices);
}
BLI_NOINLINE static void simulate_with_max_n_events(SimulationSolveContext &solve_context,
ParticleSimulationState &state,
ParticleChunkContext &particles,
int max_events)
{
Span<const ParticleEvent *> events = solve_context.influences.particle_events.lookup_as(
state.head.name);
if (events.size() == 0) {
apply_remaining_diffs(particles);
return;
}
Vector<int64_t> unfininished_pindices = particles.index_mask.indices();
for (int iteration : IndexRange(max_events)) {
UNUSED_VARS(iteration);
if (unfininished_pindices.is_empty()) {
break;
}
Vector<int64_t> new_unfinished_pindices;
ParticleChunkContext remaining_particles{particles.state,
unfininished_pindices.as_span(),
particles.attributes,
particles.integration};
simulate_to_next_event(solve_context, remaining_particles, events, new_unfinished_pindices);
unfininished_pindices = std::move(new_unfinished_pindices);
}
if (!unfininished_pindices.is_empty()) {
ParticleChunkContext remaining_particles{particles.state,
unfininished_pindices.as_span(),
particles.attributes,
particles.integration};
apply_remaining_diffs(remaining_particles);
}
}
BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context,
ParticleSimulationState &state,
MutableAttributesRef attributes,
MutableSpan<float> remaining_durations,
float end_time)
{
int particle_amount = attributes.size();
Span<const ParticleAction *> begin_actions =
solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name);
for (const ParticleAction *action : begin_actions) {
ParticleChunkContext particles{state, IndexMask(particle_amount), attributes};
ParticleActionContext action_context{solve_context, particles};
action->execute(action_context);
}
Array<float3> force_vectors{particle_amount, {0, 0, 0}};
Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as(
state.head.name);
for (const ParticleForce *force : forces) {
ParticleChunkContext particles{state, IndexMask(particle_amount), attributes};
ParticleForceContext particle_force_context{solve_context, particles, force_vectors};
force->add_force(particle_force_context);
}
MutableSpan<float3> velocities = attributes.get<float3>("Velocity");
Array<float3> position_diffs(particle_amount);
Array<float3> velocity_diffs(particle_amount);
for (int i : IndexRange(particle_amount)) {
const float time_step = remaining_durations[i];
velocity_diffs[i] = force_vectors[i] * time_step;
position_diffs[i] = (velocities[i] + velocity_diffs[i] / 2.0f) * time_step;
}
ParticleChunkIntegrationContext integration_context = {
position_diffs, velocity_diffs, remaining_durations, end_time};
ParticleChunkContext particle_chunk_context{
state, IndexMask(particle_amount), attributes, &integration_context};
simulate_with_max_n_events(solve_context, state, particle_chunk_context, 10);
Span<const ParticleAction *> end_actions =
solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name);
for (const ParticleAction *action : end_actions) {
ParticleChunkContext particles{state, IndexMask(particle_amount), attributes};
ParticleActionContext action_context{solve_context, particles};
action->execute(action_context);
}
}
BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context,
ParticleSimulationState &state,
const AttributesInfo &attributes_info)
{
CustomDataAttributesRef custom_data_attributes{
state.attributes, state.tot_particles, attributes_info};
MutableAttributesRef attributes = custom_data_attributes;
Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration());
simulate_particle_chunk(
solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop());
}
BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context,
ParticleAllocators &particle_allocators)
{
for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) {
ParticleEmitterContext emitter_context{
solve_context, particle_allocators, solve_context.solve_interval};
emitter->emit(emitter_context);
}
}
BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState &state,
ParticleAllocator &allocator)
{
CustomDataAttributesRef custom_data_attributes{
state.attributes, state.tot_particles, allocator.attributes_info()};
MutableAttributesRef attributes = custom_data_attributes;
int new_particle_amount = attributes.get<int>("Dead").count(0);
for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) {
new_particle_amount += emitted_attributes.get<int>("Dead").count(0);
}
return new_particle_amount;
}
BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationState &state,
ParticleAllocator &allocator)
{
const int new_particle_amount = count_particles_after_time_step(state, allocator);
CustomDataAttributesRef custom_data_attributes{
state.attributes, state.tot_particles, allocator.attributes_info()};
Vector<MutableAttributesRef> particle_sources;
particle_sources.append(custom_data_attributes);
particle_sources.extend(allocator.get_allocations());
CustomDataLayer *dead_layer = nullptr;
for (CustomDataLayer &layer : MutableSpan(state.attributes.layers, state.attributes.totlayer)) {
StringRefNull name = layer.name;
if (name == "Dead") {
dead_layer = &layer;
continue;
}
const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type);
GMutableSpan new_buffer{
cpp_type,
MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT),
new_particle_amount};
int current = 0;
for (MutableAttributesRef attributes : particle_sources) {
Span<int> dead_states = attributes.get<int>("Dead");
GSpan source_buffer = attributes.get(name);
BLI_assert(source_buffer.type() == cpp_type);
for (int i : attributes.index_range()) {
if (dead_states[i] == 0) {
cpp_type.copy_to_uninitialized(source_buffer[i], new_buffer[current]);
current++;
}
}
}
if (layer.data != nullptr) {
MEM_freeN(layer.data);
}
layer.data = new_buffer.data();
}
BLI_assert(dead_layer != nullptr);
if (dead_layer->data != nullptr) {
MEM_freeN(dead_layer->data);
}
dead_layer->data = MEM_callocN(sizeof(int) * new_particle_amount, AT);
state.tot_particles = new_particle_amount;
state.next_particle_id += allocator.total_allocated();
}
void initialize_simulation_states(Simulation &simulation,
Depsgraph &UNUSED(depsgraph),
const SimulationInfluences &UNUSED(influences),
const bke::PersistentDataHandleMap &UNUSED(handle_map))
{
simulation.current_simulation_time = 0.0f;
}
void solve_simulation_time_step(Simulation &simulation,
Depsgraph &depsgraph,
const SimulationInfluences &influences,
const bke::PersistentDataHandleMap &handle_map,
const DependencyAnimations &dependency_animations,
float time_step)
{
SimulationStateMap state_map;
LISTBASE_FOREACH (SimulationState *, state, &simulation.states) {
state_map.add(state);
}
SimulationSolveContext solve_context{simulation,
depsgraph,
influences,
TimeInterval(simulation.current_simulation_time, time_step),
state_map,
handle_map,
dependency_animations};
Span<ParticleSimulationState *> particle_simulation_states =
state_map.lookup<ParticleSimulationState>();
Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos;
Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map;
for (ParticleSimulationState *state : particle_simulation_states) {
const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as(
state->head.name);
auto info = std::make_unique<AttributesInfo>(builder);
ensure_attributes_exist(state, *info);
uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name);
particle_allocators_map.add_new(
state->head.name,
std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed));
attribute_infos.add_new(state->head.name, std::move(info));
}
ParticleAllocators particle_allocators{particle_allocators_map};
for (ParticleSimulationState *state : particle_simulation_states) {
const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name);
simulate_existing_particles(solve_context, *state, attributes_info);
}
run_emitters(solve_context, particle_allocators);
for (ParticleSimulationState *state : particle_simulation_states) {
ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
for (MutableAttributesRef attributes : allocator.get_allocations()) {
Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as(
state->head.name);
for (const ParticleAction *action : actions) {
ParticleChunkContext chunk_context{*state, IndexRange(attributes.size()), attributes};
ParticleActionContext action_context{solve_context, chunk_context};
action->execute(action_context);
}
}
}
for (ParticleSimulationState *state : particle_simulation_states) {
ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name);
for (MutableAttributesRef attributes : allocator.get_allocations()) {
Array<float> remaining_durations(attributes.size());
Span<float> birth_times = attributes.get<float>("Birth Time");
const float end_time = solve_context.solve_interval.stop();
for (int i : attributes.index_range()) {
remaining_durations[i] = end_time - birth_times[i];
}
simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time);
}
remove_dead_and_add_new_particles(*state, allocator);
}
simulation.current_simulation_time = solve_context.solve_interval.stop();
}
} // namespace blender::sim

View File

@ -1,37 +0,0 @@
/*
* 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.
*/
#pragma once
#include "simulation_collect_influences.hh"
struct Depsgraph;
namespace blender::sim {
void initialize_simulation_states(Simulation &simulation,
Depsgraph &depsgraph,
const SimulationInfluences &influences,
const bke::PersistentDataHandleMap &handle_map);
void solve_simulation_time_step(Simulation &simulation,
Depsgraph &depsgraph,
const SimulationInfluences &influences,
const bke::PersistentDataHandleMap &handle_map,
const DependencyAnimations &dependency_animations,
float time_step);
} // namespace blender::sim

View File

@ -1,57 +0,0 @@
/*
* 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 "simulation_solver_influences.hh"
#include "DNA_object_types.h"
namespace blender::sim {
ParticleForce::~ParticleForce()
{
}
ParticleEmitter::~ParticleEmitter()
{
}
ParticleAction::~ParticleAction()
{
}
ParticleEvent::~ParticleEvent()
{
}
DependencyAnimations::~DependencyAnimations()
{
}
bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const
{
return false;
}
void DependencyAnimations::get_object_transforms(Object &object,
Span<float> simulation_times,
MutableSpan<float4x4> r_transforms) const
{
assert_same_size(simulation_times, r_transforms);
float4x4 world_matrix = object.obmat;
r_transforms.fill(world_matrix);
}
} // namespace blender::sim

View File

@ -1,234 +0,0 @@
/*
* 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.
*/
#pragma once
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_span.hh"
#include "DNA_simulation_types.h"
#include "FN_attributes_ref.hh"
#include "BKE_persistent_data_handle.hh"
#include "BKE_simulation.h"
#include "particle_allocator.hh"
#include "time_interval.hh"
namespace blender::sim {
using fn::AttributesInfo;
using fn::AttributesInfoBuilder;
using fn::AttributesRef;
using fn::CPPType;
using fn::GMutableSpan;
using fn::GSpan;
using fn::MutableAttributesRef;
struct ParticleActionContext;
struct ParticleEmitterContext;
struct ParticleEventFilterContext;
struct ParticleForceContext;
class ParticleEmitter {
public:
virtual ~ParticleEmitter();
virtual void emit(ParticleEmitterContext &context) const = 0;
};
class ParticleForce {
public:
virtual ~ParticleForce();
virtual void add_force(ParticleForceContext &context) const = 0;
};
class ParticleAction {
public:
virtual ~ParticleAction();
virtual void execute(ParticleActionContext &context) const = 0;
};
class ParticleEvent {
public:
virtual ~ParticleEvent();
virtual void filter(ParticleEventFilterContext &context) const = 0;
virtual void execute(ParticleActionContext &context) const = 0;
};
struct SimulationInfluences {
MultiValueMap<std::string, const ParticleForce *> particle_forces;
MultiValueMap<std::string, const ParticleAction *> particle_birth_actions;
MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions;
MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions;
MultiValueMap<std::string, const ParticleEvent *> particle_events;
Map<std::string, AttributesInfoBuilder *> particle_attributes_builder;
Vector<const ParticleEmitter *> particle_emitters;
};
class SimulationStateMap {
private:
Map<StringRefNull, SimulationState *> states_by_name_;
MultiValueMap<StringRefNull, SimulationState *> states_by_type_;
public:
void add(SimulationState *state)
{
states_by_name_.add_new(state->name, state);
states_by_type_.add(state->type, state);
}
template<typename StateType> StateType *lookup(StringRef name) const
{
const char *type = BKE_simulation_get_state_type_name<StateType>();
return reinterpret_cast<StateType *>(this->lookup_name_type(name, type));
}
template<typename StateType> Span<StateType *> lookup() const
{
const char *type = BKE_simulation_get_state_type_name<StateType>();
return this->lookup_type(type).cast<StateType *>();
}
SimulationState *lookup_name_type(StringRef name, StringRef type) const
{
SimulationState *state = states_by_name_.lookup_default_as(name, nullptr);
if (state == nullptr) {
return nullptr;
}
if (state->type == type) {
return state;
}
return nullptr;
}
Span<SimulationState *> lookup_type(StringRef type) const
{
return states_by_type_.lookup_as(type);
}
};
class DependencyAnimations {
public:
~DependencyAnimations();
virtual bool is_object_transform_changing(Object &object) const;
virtual void get_object_transforms(Object &object,
Span<float> simulation_times,
MutableSpan<float4x4> r_transforms) const;
};
struct SimulationSolveContext {
Simulation &simulation;
Depsgraph &depsgraph;
const SimulationInfluences &influences;
TimeInterval solve_interval;
const SimulationStateMap &state_map;
const bke::PersistentDataHandleMap &handle_map;
const DependencyAnimations &dependency_animations;
};
class ParticleAllocators {
private:
Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_;
public:
ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators)
: allocators_(allocators)
{
}
ParticleAllocator *try_get_allocator(StringRef particle_simulation_name)
{
auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name);
if (ptr != nullptr) {
return ptr->get();
}
else {
return nullptr;
}
}
};
struct ParticleChunkIntegrationContext {
MutableSpan<float3> position_diffs;
MutableSpan<float3> velocity_diffs;
MutableSpan<float> durations;
float end_time;
};
struct ParticleChunkContext {
ParticleSimulationState &state;
IndexMask index_mask;
MutableAttributesRef attributes;
ParticleChunkIntegrationContext *integration = nullptr;
void update_diffs_after_velocity_change()
{
if (integration == nullptr) {
return;
}
Span<float> remaining_durations = integration->durations;
MutableSpan<float3> position_diffs = integration->position_diffs;
Span<float3> velocities = attributes.get<float3>("Velocity");
for (int i : index_mask) {
const float duration = remaining_durations[i];
/* This is certainly not a perfect way to "re-integrate" the velocity, but it should be good
* enough for most use cases. Changing the velocity in an instant is not physically correct
* anyway. */
position_diffs[i] = velocities[i] * duration;
}
}
};
struct ParticleEmitterContext {
SimulationSolveContext &solve_context;
ParticleAllocators &particle_allocators;
TimeInterval emit_interval;
template<typename StateType> StateType *lookup_state(StringRef name)
{
return solve_context.state_map.lookup<StateType>(name);
}
ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name)
{
return particle_allocators.try_get_allocator(particle_simulation_name);
}
};
struct ParticleForceContext {
SimulationSolveContext &solve_context;
ParticleChunkContext &particles;
MutableSpan<float3> force_dst;
};
struct ParticleActionContext {
SimulationSolveContext &solve_context;
ParticleChunkContext &particles;
};
struct ParticleEventFilterContext {
SimulationSolveContext &solve_context;
ParticleChunkContext &particles;
MutableSpan<float> factor_dst;
};
} // namespace blender::sim

View File

@ -1,357 +0,0 @@
/*
* 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 "SIM_simulation_update.hh"
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_object.h"
#include "BKE_simulation.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
#include "DEG_depsgraph_query.h"
#include "BLI_array.hh"
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_rand.h"
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "NOD_node_tree_dependencies.hh"
#include "particle_function.hh"
#include "simulation_collect_influences.hh"
#include "simulation_solver.hh"
namespace blender::sim {
static void copy_states_to_cow(const Simulation *simulation_orig, Simulation *simulation_cow)
{
BKE_simulation_state_remove_all(simulation_cow);
simulation_cow->current_frame = simulation_orig->current_frame;
LISTBASE_FOREACH (const SimulationState *, state_orig, &simulation_orig->states) {
SimulationState *state_cow = BKE_simulation_state_add(
simulation_cow, state_orig->type, state_orig->name);
BKE_simulation_state_copy_data(state_orig, state_cow);
}
}
static void remove_unused_states(Simulation *simulation, const RequiredStates &required_states)
{
LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) {
if (!required_states.is_required(state->name, state->type)) {
BKE_simulation_state_remove(simulation, state);
}
}
}
static void add_missing_states(Simulation *simulation, const RequiredStates &required_states)
{
for (auto &&item : required_states.states().items()) {
const char *name = item.key.c_str();
const char *type = item.value;
SimulationState *state = BKE_simulation_state_try_find_by_name_and_type(
simulation, name, type);
if (state == nullptr) {
BKE_simulation_state_add(simulation, type, name);
}
}
}
static void reinitialize_empty_simulation_states(Simulation *simulation,
const RequiredStates &required_states)
{
remove_unused_states(simulation, required_states);
BKE_simulation_state_reset_all(simulation);
add_missing_states(simulation, required_states);
}
static void update_simulation_state_list(Simulation *simulation,
const RequiredStates &required_states)
{
remove_unused_states(simulation, required_states);
add_missing_states(simulation, required_states);
}
class SampledDependencyAnimations : public DependencyAnimations {
private:
TimeInterval simulation_time_interval_;
MultiValueMap<Object *, float4x4> object_transforms_cache_;
public:
SampledDependencyAnimations(TimeInterval simulation_time_interval)
: simulation_time_interval_(simulation_time_interval)
{
}
void add_object_transforms(Object &object, Span<float4x4> transforms)
{
object_transforms_cache_.add_multiple(&object, transforms);
}
bool is_object_transform_changing(Object &object) const
{
return object_transforms_cache_.lookup(&object).size() >= 2;
}
void get_object_transforms(Object &object,
Span<float> simulation_times,
MutableSpan<float4x4> r_transforms) const
{
assert_same_size(simulation_times, r_transforms);
Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object);
if (cached_transforms.size() == 0) {
r_transforms.fill(object.obmat);
return;
}
if (cached_transforms.size() == 1) {
r_transforms.fill(cached_transforms[0]);
return;
}
for (int i : simulation_times.index_range()) {
const float simulation_time = simulation_times[i];
if (simulation_time <= simulation_time_interval_.start()) {
r_transforms[i] = cached_transforms.first();
continue;
}
if (simulation_time >= simulation_time_interval_.stop()) {
r_transforms[i] = cached_transforms.last();
continue;
}
const float factor = simulation_time_interval_.factor_at_time(simulation_time);
BLI_assert(factor > 0.0f && factor < 1.0f);
const float scaled_factor = factor * (cached_transforms.size() - 1);
const int lower_sample = static_cast<int>(scaled_factor);
const int upper_sample = lower_sample + 1;
const float mix_factor = scaled_factor - lower_sample;
r_transforms[i] = float4x4::interpolate(
cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor);
}
}
};
static void sample_object_transforms(Object &object,
Depsgraph &depsgraph,
Scene &scene,
TimeInterval scene_frame_interval,
MutableSpan<float4x4> r_transforms)
{
if (r_transforms.size() == 0) {
return;
}
if (r_transforms.size() == 1) {
r_transforms[0] = object.obmat;
return;
}
Array<float> frames(r_transforms.size());
scene_frame_interval.compute_uniform_samples(frames);
for (int i : frames.index_range()) {
float frame = frames[i];
const int recursion_depth = 5;
BKE_object_modifier_update_subframe(
&depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None);
r_transforms[i] = object.obmat;
}
}
template<typename T> static bool all_values_equal(Span<T> values)
{
if (values.size() == 0) {
return true;
}
for (const T &value : values.drop_front(1)) {
if (value != values[0]) {
return false;
}
}
return true;
}
static void prepare_dependency_animations(Depsgraph &depsgraph,
Scene &scene,
Simulation &simulation,
TimeInterval scene_frame_interval,
SampledDependencyAnimations &r_dependency_animations)
{
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) {
ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id);
if (id_cow == nullptr) {
continue;
}
if (GS(id_cow->name) != ID_OB) {
continue;
}
Object &object_cow = *reinterpret_cast<Object *>(id_cow);
constexpr int sample_count = 10;
Array<float4x4, sample_count> transforms(sample_count);
sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms);
/* If all samples are the same, only store one. */
Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ?
transforms.as_span().take_front(1) :
transforms.as_span();
r_dependency_animations.add_object_transforms(object_cow, transforms_to_use);
}
}
void update_simulation_in_depsgraph(Depsgraph *depsgraph,
Scene *scene_cow,
Simulation *simulation_cow)
{
int current_frame = scene_cow->r.cfra;
if (simulation_cow->current_frame == current_frame) {
return;
}
/* Below we modify the original state/cache. Only the active depsgraph is allowed to do that. */
if (!DEG_is_active(depsgraph)) {
return;
}
Simulation *simulation_orig = reinterpret_cast<Simulation *>(
DEG_get_original_id(&simulation_cow->id));
ResourceCollector resources;
SimulationInfluences influences;
RequiredStates required_states;
collect_simulation_influences(*simulation_cow, resources, influences, required_states);
bke::PersistentDataHandleMap handle_map;
LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) {
ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id);
if (id_cow != nullptr) {
handle_map.add(dependency->handle, *id_cow);
}
}
if (current_frame == 1) {
reinitialize_empty_simulation_states(simulation_orig, required_states);
initialize_simulation_states(*simulation_orig, *depsgraph, influences, handle_map);
simulation_orig->current_frame = 1;
copy_states_to_cow(simulation_orig, simulation_cow);
}
else if (current_frame == simulation_orig->current_frame + 1) {
update_simulation_state_list(simulation_orig, required_states);
const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base;
const float time_step = 1.0f / fps;
TimeInterval scene_frame_interval(current_frame - 1, 1);
TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step);
SampledDependencyAnimations dependency_animations{simulation_time_interval};
prepare_dependency_animations(
*depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations);
solve_simulation_time_step(
*simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step);
simulation_orig->current_frame = current_frame;
copy_states_to_cow(simulation_orig, simulation_cow);
}
}
/* Returns true when dependencies have changed. */
bool update_simulation_dependencies(Simulation *simulation)
{
nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies(
*simulation->nodetree);
ListBase *dependency_list = &simulation->dependencies;
bool dependencies_changed = false;
Map<ID *, SimulationDependency *> dependency_by_id;
Map<SimulationDependency *, int> old_flag_by_dependency;
Set<int> used_handles;
/* Remove unused handle items and clear flags that are reinitialized later. */
LISTBASE_FOREACH_MUTABLE (SimulationDependency *, dependency, dependency_list) {
if (dependencies.depends_on(dependency->id)) {
dependency_by_id.add_new(dependency->id, dependency);
used_handles.add_new(dependency->handle);
old_flag_by_dependency.add_new(dependency, dependency->flag);
dependency->flag &= ~(SIM_DEPENDS_ON_TRANSFORM | SIM_DEPENDS_ON_GEOMETRY);
}
else {
if (dependency->id != nullptr) {
id_us_min(dependency->id);
}
BLI_remlink(dependency_list, dependency);
MEM_freeN(dependency);
dependencies_changed = true;
}
}
/* Add handle items for new id dependencies. */
int next_handle = 0;
for (ID *id : dependencies.id_dependencies()) {
dependency_by_id.lookup_or_add_cb(id, [&]() {
while (used_handles.contains(next_handle)) {
next_handle++;
}
used_handles.add_new(next_handle);
SimulationDependency *dependency = static_cast<SimulationDependency *>(
MEM_callocN(sizeof(*dependency), AT));
id_us_plus(id);
dependency->id = id;
dependency->handle = next_handle;
BLI_addtail(dependency_list, dependency);
return dependency;
});
}
/* Set appropriate dependency flags. */
for (Object *object : dependencies.transform_dependencies()) {
SimulationDependency *dependency = dependency_by_id.lookup(&object->id);
dependency->flag |= SIM_DEPENDS_ON_TRANSFORM;
}
for (Object *object : dependencies.geometry_dependencies()) {
SimulationDependency *dependency = dependency_by_id.lookup(&object->id);
dependency->flag |= SIM_DEPENDS_ON_GEOMETRY;
}
if (!dependencies_changed) {
/* Check if any flags have changed. */
LISTBASE_FOREACH (SimulationDependency *, dependency, dependency_list) {
uint32_t old_flag = old_flag_by_dependency.lookup_default(dependency, 0);
uint32_t new_flag = dependency->flag;
if (old_flag != new_flag) {
dependencies_changed = true;
break;
}
}
}
return dependencies_changed;
}
} // namespace blender::sim

View File

@ -1,90 +0,0 @@
/*
* 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.
*/
#pragma once
#include "BLI_utildefines.h"
namespace blender::sim {
/**
* The start time is exclusive and the end time is inclusive. If the duration is zero, the interval
* describes a single point in time.
*/
class TimeInterval {
private:
float start_;
float duration_;
public:
TimeInterval(float start, float duration) : start_(start), duration_(duration)
{
BLI_assert(duration_ >= 0.0f);
}
float start() const
{
return start_;
}
float stop() const
{
return start_ + duration_;
}
float duration() const
{
return duration_;
}
float time_at_factor(float factor) const
{
return start_ + factor * duration_;
}
float factor_at_time(float time) const
{
BLI_assert(duration_ > 0.0f);
return (time - start_) / duration_;
}
float safe_factor_at_time(float time) const
{
if (duration_ > 0.0f) {
return this->factor_at_time(time);
}
return 0.0f;
}
void compute_uniform_samples(MutableSpan<float> r_samples) const
{
int64_t amount = r_samples.size();
if (amount == 0) {
return;
}
if (amount == 1) {
r_samples[0] = this->time_at_factor(0.5f);
return;
}
const float step = duration_ / (float)(amount - 1);
for (int64_t i : r_samples.index_range()) {
r_samples[i] = start_ + i * step;
}
}
};
} // namespace blender::sim